From 6738499e07d811afa137d84cefeeeb2bfc45cfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Thu, 31 Jul 2025 22:51:00 +0200 Subject: [PATCH 01/37] Fix: Regex for filtering schedule --- src/main/java/org/pkwmtt/timetable/TimetableService.java | 1 + src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index 15ab483..0668066 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -71,6 +71,7 @@ public TimetableDTO getFilteredGeneralGroupSchedule(String generalGroupName, Lis for (var day : schedule) sub.forEach(day::filterByGroup); + System.out.println(); schedule.forEach(DayOfWeekDTO::deleteSubjectTypesFromNames); return new TimetableDTO(generalGroupName, schedule); diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index 50d6403..e8ba51d 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -96,7 +96,7 @@ private List filter(List list, String groupName, String * @return true if no non-target subgroup codes are present */ private boolean hasOnlyTargetGroup(String element, String groupName, String targetNumber) { - Pattern pattern = Pattern.compile(String.format("\\b[%s]0[1-9]\\b", groupName)); + Pattern pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); Matcher matcher = pattern.matcher(element); if (!matcher.find()) return true; From 1c9058eabdd3014562d81750c217e9ab9728f87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:42:46 +0200 Subject: [PATCH 02/37] Clear subject name from unnecessary characters --- .../pkwmtt/{timetable => }/enums/SubjectType.java | 2 +- .../java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java | 4 ++-- .../java/org/pkwmtt/timetable/dto/SubjectDTO.java | 13 ++++++++++--- .../timetable/parser/TimetableParserService.java | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) rename src/main/java/org/pkwmtt/{timetable => }/enums/SubjectType.java (78%) diff --git a/src/main/java/org/pkwmtt/timetable/enums/SubjectType.java b/src/main/java/org/pkwmtt/enums/SubjectType.java similarity index 78% rename from src/main/java/org/pkwmtt/timetable/enums/SubjectType.java rename to src/main/java/org/pkwmtt/enums/SubjectType.java index bcb41af..f43282e 100644 --- a/src/main/java/org/pkwmtt/timetable/enums/SubjectType.java +++ b/src/main/java/org/pkwmtt/enums/SubjectType.java @@ -1,4 +1,4 @@ -package org.pkwmtt.timetable.enums; +package org.pkwmtt.enums; public enum SubjectType { LECTURE, diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index e8ba51d..ea9bd2f 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -32,8 +32,8 @@ public void add(SubjectDTO subjectDTO, boolean isNotOdd) { public void deleteSubjectTypesFromNames() { - even.forEach(SubjectDTO::deleteTypeFromName); - odd.forEach(SubjectDTO::deleteTypeFromName); + even.forEach(SubjectDTO::deleteTypeAndUnnecessaryCharactersFromName); + odd.forEach(SubjectDTO::deleteTypeAndUnnecessaryCharactersFromName); } /** diff --git a/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java b/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java index d3a9f55..bb3f214 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java @@ -1,7 +1,9 @@ package org.pkwmtt.timetable.dto; import lombok.*; -import org.pkwmtt.timetable.enums.SubjectType; +import org.pkwmtt.enums.SubjectType; + +import java.util.regex.Pattern; @Builder @Getter @@ -14,8 +16,13 @@ public class SubjectDTO { private SubjectType type; - public void deleteTypeFromName() { + public void deleteTypeAndUnnecessaryCharactersFromName() { if (name.contains(" ")) - this.name = name.substring(0,name.indexOf(' ')); + this.name = name.substring(0, name.indexOf(' ')); + + name = name + .replaceAll("_", " ") + .replaceAll(Pattern.quote("("), "") + .replaceAll(Pattern.quote(")"), ""); } } diff --git a/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java b/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java index 085b8b8..9be7295 100644 --- a/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java +++ b/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java @@ -7,7 +7,7 @@ import org.jsoup.select.Elements; import org.pkwmtt.timetable.dto.DayOfWeekDTO; import org.pkwmtt.timetable.dto.SubjectDTO; -import org.pkwmtt.timetable.enums.SubjectType; +import org.pkwmtt.enums.SubjectType; import org.springframework.stereotype.Service; import java.util.ArrayList; From 9febb6bcd8175b25759325596b6afdfb066cfa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:42:06 +0200 Subject: [PATCH 03/37] Move exception handling to GlobalExceptionHandler --- pom.xml | 2 +- .../GlobalExceptionHandler.java | 32 +++++++++++++++++++ .../pkwmtt/timetable/TimetableController.java | 18 ----------- 3 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java diff --git a/pom.xml b/pom.xml index fd73cac..64dcc1a 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.6.0 + 2.8.9 com.mysql diff --git a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java new file mode 100644 index 0000000..129aea9 --- /dev/null +++ b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java @@ -0,0 +1,32 @@ +package org.pkwmtt.controllerAdvice; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.pkwmtt.exceptions.ErrorResponseDTO; +import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; +import org.pkwmtt.exceptions.WebPageContentNotAvailableException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(WebPageContentNotAvailableException.class) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + public ResponseEntity handleWebPageContentNotAvailableException(WebPageContentNotAvailableException e) { + return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); + } + + @ExceptionHandler(JsonProcessingException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity handleJsonProcessingException() { + return new ResponseEntity<>(new ErrorResponseDTO("Json Processing Failed"), HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(SpecifiedGeneralGroupDoesntExistsException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsException(SpecifiedGeneralGroupDoesntExistsException e) { + return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.BAD_REQUEST); + } +} diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index 613ad37..9c3a51a 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -2,11 +2,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.RequiredArgsConstructor; -import org.pkwmtt.exceptions.ErrorResponseDTO; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.pkwmtt.timetable.dto.TimetableDTO; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -75,22 +73,6 @@ public ResponseEntity> getListOfAvailableGroups(@PathVariable Strin return ResponseEntity.ok(service.getAvailableSubGroups(generalGroupName.toUpperCase())); } - @ExceptionHandler(WebPageContentNotAvailableException.class) - @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) - public ResponseEntity handleWebPageContentNotAvailableException(WebPageContentNotAvailableException e) { - return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); - } - - @ExceptionHandler(JsonProcessingException.class) - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ResponseEntity handleJsonProcessingException() { - return new ResponseEntity<>(new ErrorResponseDTO(""), HttpStatus.INTERNAL_SERVER_ERROR); - } - @ExceptionHandler(SpecifiedGeneralGroupDoesntExistsException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsException(SpecifiedGeneralGroupDoesntExistsException e) { - return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.BAD_REQUEST); - } } From 2cb0820653a84ed65a5e25d1bce3798c09d5f424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:42:43 +0200 Subject: [PATCH 04/37] Delete duplicated dependency --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 64dcc1a..14f842e 100644 --- a/pom.xml +++ b/pom.xml @@ -117,11 +117,6 @@ springdoc-openapi-starter-webmvc-ui 2.8.9 - - com.mysql - mysql-connector-j - runtime - From f1319f8336b8d2e95f555931da42b4751aa80680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:52:18 +0200 Subject: [PATCH 05/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbbb10e..383f2d9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ### 1. Clone the repository ```shell -docker pull ghcr.io/pkttteam/pkwmtt-backend:[PACKAGE_NUMBER] +docker pull ghcr.io/pkttteam/pkwmtt-backend:latest ``` ### 2. Create a `.env` file From 2bd8d2dba0fb5f4adbb29c7c40c6736752e48f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:26:22 +0200 Subject: [PATCH 06/37] Add logging errors to file app.log --- logs/app.log | 2 + pom.xml | 329 +++++++++--------- .../HighlightingCompositeLogConverter.java | 19 + .../java/org/pkwmtt/config/StartupConfig.java | 1 + .../GlobalExceptionHandler.java | 7 +- src/main/resources/application.properties | 3 + src/main/resources/logback.xml | 43 +++ 7 files changed, 247 insertions(+), 157 deletions(-) create mode 100644 logs/app.log create mode 100644 src/main/java/org/pkwmtt/config/HighlightingCompositeLogConverter.java create mode 100644 src/main/resources/logback.xml diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..84af5af --- /dev/null +++ b/logs/app.log @@ -0,0 +1,2 @@ +2025-08-04 21:26:19 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-06 14:57:49 ERROR org.pkwmtt.config.StartupConfig - TEST diff --git a/pom.xml b/pom.xml index 14f842e..f84ab2c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,169 +1,186 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.5.3 - - - org.pkwmtt - PKWMTT-backend - 0.0.1-SNAPSHOT - PKWMTT-backend - PKWMTT-backend - - - - - - - - - - - - - - - 21 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.3 + + + org.pkwmtt + PKWMTT-backend + 0.1.0-ALPHA + PKWMTT-backend + PKWM App (Server) – timetable, exam calendar and ECTS calculator for students of Mechanical Engineering @ Cracow University of Technology + https://github.com/PKTTTeam/PKWMTT-backend + + + MIT + + + + + Mikołaj Florczak + https://github.com/florczaq + PKWM Mobile App Team + https://github.com/PKTTTeam + + + + + + + + + + + + 21 + + - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-data-jpa - + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-jpa + - - - org.projectlombok - lombok - true - + + + org.projectlombok + lombok + true + - - - com.h2database - h2 - runtime - + + + com.h2database + h2 + runtime + - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.security - spring-security-test - test - - - - junit - junit - 4.13.1 - - - org.mockito - mockito-all - 1.10.19 - - - org.mockito - mockito-core - 5.18.0 - + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + + + + junit + junit + 4.13.1 + + + org.mockito + mockito-all + 1.10.19 + + + org.mockito + mockito-core + 5.18.0 + - - - org.jsoup - jsoup - 1.15.3 - + + + org.jsoup + jsoup + 1.15.3 + - - - com.github.ben-manes.caffeine - caffeine - 3.1.8 - - - org.springframework.boot - spring-boot-starter-cache - + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + + org.springframework.boot + spring-boot-starter-cache + - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.8.9 - + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.9 + - - - com.mysql - mysql-connector-j - - - org.springframework.boot - spring-boot-starter-actuator - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - -javaagent:${settings.localRepository}/org/mockito/mockito-core/5.18.0/mockito-core-5.18.0.jar - - + + + com.mysql + mysql-connector-j + + + org.springframework.boot + spring-boot-starter-actuator + + + + ch.qos.logback + logback-classic + 1.5.13 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + -javaagent:${settings.localRepository}/org/mockito/mockito-core/5.18.0/mockito-core-5.18.0.jar + + + - - + + diff --git a/src/main/java/org/pkwmtt/config/HighlightingCompositeLogConverter.java b/src/main/java/org/pkwmtt/config/HighlightingCompositeLogConverter.java new file mode 100644 index 0000000..dc2440b --- /dev/null +++ b/src/main/java/org/pkwmtt/config/HighlightingCompositeLogConverter.java @@ -0,0 +1,19 @@ +package org.pkwmtt.config; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.color.ANSIConstants; +import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase; + +public class HighlightingCompositeLogConverter extends ForegroundCompositeConverterBase { + + @Override + protected String getForegroundColorCode(ILoggingEvent event) { + return switch (event.getLevel().toInt()) { + case Level.ERROR_INT -> ANSIConstants.BOLD + ANSIConstants.RED_FG; + case Level.WARN_INT -> ANSIConstants.RED_FG; + case Level.INFO_INT -> ANSIConstants.CYAN_FG; + default -> ANSIConstants.DEFAULT_FG; + }; + } +} diff --git a/src/main/java/org/pkwmtt/config/StartupConfig.java b/src/main/java/org/pkwmtt/config/StartupConfig.java index 234dee7..aa8ce3a 100644 --- a/src/main/java/org/pkwmtt/config/StartupConfig.java +++ b/src/main/java/org/pkwmtt/config/StartupConfig.java @@ -23,6 +23,7 @@ public class StartupConfig { @EventListener(ContextRefreshedEvent.class) public void onApplicationEvent() { + log.error("TEST"); try { if (port.isEmpty() || address.isEmpty()) throw new Exception(); diff --git a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java index 129aea9..673b7ca 100644 --- a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java +++ b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java @@ -1,6 +1,7 @@ package org.pkwmtt.controllerAdvice; import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; import org.pkwmtt.exceptions.ErrorResponseDTO; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; @@ -10,17 +11,21 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { + @ExceptionHandler(WebPageContentNotAvailableException.class) @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) public ResponseEntity handleWebPageContentNotAvailableException(WebPageContentNotAvailableException e) { + log.error("SERVICE_UNAVAILABLE # " + e.getMessage() + " # " + e.getCause()); return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); } @ExceptionHandler(JsonProcessingException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ResponseEntity handleJsonProcessingException() { + public ResponseEntity handleJsonProcessingException(JsonProcessingException e) { + log.error("INTERNAL_SERVER_ERROR # " + e.getMessage() + " # " + e.getCause()); return new ResponseEntity<>(new ErrorResponseDTO("Json Processing Failed"), HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f9244d0..911660b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,3 +10,6 @@ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false spring.jpa.hibernate.ddl-auto=none spring.datasource.hikari.initialization-fail-timeout=0 + +logging.file.name=logs/app.log +logging.file.path=logs \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..dcbbeca --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + true + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n + + + + INFO + + + + + + logs/app.log + true + + %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + + + ERROR + + + + + + + + + + From 3e2b0d657b2f25b1981c2ff512567a49dae4a7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:27:33 +0200 Subject: [PATCH 07/37] Created CacheInspectorController for cache debugging --- .../java/org/pkwmtt/cache/CacheInspector.java | 14 ++++-- .../pkwmtt/temp/CacheInspectorController.java | 43 +++++++++++++++++++ .../pkwmtt/timetable/TimetableController.java | 1 + .../pkwmtt/timetable/TimetableService.java | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/pkwmtt/temp/CacheInspectorController.java diff --git a/src/main/java/org/pkwmtt/cache/CacheInspector.java b/src/main/java/org/pkwmtt/cache/CacheInspector.java index 4dd315e..b05e9df 100644 --- a/src/main/java/org/pkwmtt/cache/CacheInspector.java +++ b/src/main/java/org/pkwmtt/cache/CacheInspector.java @@ -28,13 +28,21 @@ public Map getAllEntries(String cacheName) { return nativeCache.asMap(); } - public void printAllEntries(String cacheName) { + public String printAllEntries(String cacheName) { service.getListOfHours(); service.getGeneralGroupSchedule("12K1"); service.getGeneralGroupsList(); - + var s = new StringBuilder(); getAllEntries(cacheName).forEach((key, value) -> - System.out.println("Cache[" + cacheName + "] " + key + " -> " + value) + s + .append("Cache[") + .append(cacheName) + .append("] ") + .append(key) + .append(" -> ") + .append(value) + .append("\n") ); + return s.toString(); } } diff --git a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java new file mode 100644 index 0000000..2fd9a68 --- /dev/null +++ b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java @@ -0,0 +1,43 @@ +package org.pkwmtt.temp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.cache.CacheInspector; +import org.pkwmtt.timetable.CacheableTimetableService; +import org.pkwmtt.timetable.TimetableService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/cache") +@RequiredArgsConstructor +public class CacheInspectorController { + private final TimetableService service; + private final CacheableTimetableService cacheableTimetableService; + private final CacheInspector cacheInspector; + + @GetMapping + public String temp() { + List generalGroups = cacheableTimetableService.getGeneralGroupsList().keySet().stream().toList(); + StringBuilder stringBuilder = new StringBuilder(); + + generalGroups.forEach(group -> { + try { + stringBuilder.append(group).append(": "); + stringBuilder.append(service.getAvailableSubGroups(group).isEmpty() ? "\t\tBAD" : "\tGOOD"); + stringBuilder.append("\n"); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + + return String.format( + "%s\n\n%s", + stringBuilder, + cacheInspector.printAllEntries("timetables") + ); + } +} diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index 9c3a51a..69f277b 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -32,6 +32,7 @@ public ResponseEntity getGeneralGroupSchedule(@PathVariable String if (sub == null || sub.isEmpty()) return ResponseEntity.ok(cacheableService.getGeneralGroupSchedule(generalGroupName)); + //todo delete sub = sub.stream().map(String::toUpperCase).toList(); return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule(generalGroupName.toUpperCase(), sub)); diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index 0668066..390157d 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -28,6 +28,7 @@ public class TimetableService { * @throws JsonProcessingException if timetable conversion to JSON fails */ public List getAvailableSubGroups(String generalGroupName) + //TODO group name to upper case throws JsonProcessingException, SpecifiedGeneralGroupDoesntExistsException, WebPageContentNotAvailableException { ObjectMapper mapper = new ObjectMapper(); TimetableDTO timetable = cacheableTimetableService.getGeneralGroupSchedule(generalGroupName); From aa0328d3cb5072adec943e0dd25a31c2816aeccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 8 Aug 2025 18:19:10 +0200 Subject: [PATCH 08/37] Rename CachableTimetableService to TimetableCacheService --- src/main/java/org/pkwmtt/cache/CacheInspector.java | 4 ++-- src/main/java/org/pkwmtt/temp/CacheInspectorController.java | 4 ++-- ...heableTimetableService.java => TimetableCacheService.java} | 2 +- src/main/java/org/pkwmtt/timetable/TimetableController.java | 2 +- src/main/java/org/pkwmtt/timetable/TimetableService.java | 2 +- src/test/java/org/pkwmtt/cache/CacheConfigTest.java | 4 ++-- .../org/pkwmtt/timetable/CacheableTimetableServiceTest.java | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) rename src/main/java/org/pkwmtt/timetable/{CacheableTimetableService.java => TimetableCacheService.java} (98%) diff --git a/src/main/java/org/pkwmtt/cache/CacheInspector.java b/src/main/java/org/pkwmtt/cache/CacheInspector.java index b05e9df..7e92848 100644 --- a/src/main/java/org/pkwmtt/cache/CacheInspector.java +++ b/src/main/java/org/pkwmtt/cache/CacheInspector.java @@ -2,7 +2,7 @@ import com.github.benmanes.caffeine.cache.Cache; import lombok.RequiredArgsConstructor; -import org.pkwmtt.timetable.CacheableTimetableService; +import org.pkwmtt.timetable.TimetableCacheService; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.stereotype.Component; @@ -14,7 +14,7 @@ public class CacheInspector { private final CacheManager cacheManager; - private final CacheableTimetableService service; + private final TimetableCacheService service; public Map getAllEntries(String cacheName) { diff --git a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java index 2fd9a68..e8470f2 100644 --- a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java +++ b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.RequiredArgsConstructor; import org.pkwmtt.cache.CacheInspector; -import org.pkwmtt.timetable.CacheableTimetableService; +import org.pkwmtt.timetable.TimetableCacheService; import org.pkwmtt.timetable.TimetableService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class CacheInspectorController { private final TimetableService service; - private final CacheableTimetableService cacheableTimetableService; + private final TimetableCacheService cacheableTimetableService; private final CacheInspector cacheInspector; @GetMapping diff --git a/src/main/java/org/pkwmtt/timetable/CacheableTimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java similarity index 98% rename from src/main/java/org/pkwmtt/timetable/CacheableTimetableService.java rename to src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index be76b87..2d73cb3 100644 --- a/src/main/java/org/pkwmtt/timetable/CacheableTimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -18,7 +18,7 @@ @Service @RequiredArgsConstructor @CacheConfig(cacheNames = "timetables") -public class CacheableTimetableService { +public class TimetableCacheService { private final TimetableParserService parser; /** diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index 69f277b..fccdb6c 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class TimetableController { private final TimetableService service; - private final CacheableTimetableService cacheableService; + private final TimetableCacheService cacheableService; /** * Provide schedule of specified group and filters if all provided diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index 390157d..ee1951f 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -18,7 +18,7 @@ @Service @RequiredArgsConstructor public class TimetableService { - private final CacheableTimetableService cacheableTimetableService; + private final TimetableCacheService cacheableTimetableService; /** * Parses the timetable JSON to extract subgroup identifiers like K01, P03, GL04 using regex. diff --git a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java index 2327686..fdad1ca 100644 --- a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java +++ b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java @@ -1,7 +1,7 @@ package org.pkwmtt.cache; import org.junit.jupiter.api.Test; -import org.pkwmtt.timetable.CacheableTimetableService; +import org.pkwmtt.timetable.TimetableCacheService; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -17,7 +17,7 @@ @SpringBootTest class CacheConfigTest { @Autowired - private CacheableTimetableService service; + private TimetableCacheService service; @Autowired private CacheManager cacheManager; diff --git a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java b/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java index 6781569..55f6586 100644 --- a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java @@ -10,7 +10,7 @@ @SpringBootTest class CacheableTimetableServiceTest { @Autowired - CacheableTimetableService service; + TimetableCacheService service; @Test public void checkIfHoursListBodyIsNotNull() { From da31231dad2af5a508686e585713c4dadf882116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:39:13 +0200 Subject: [PATCH 09/37] Fix: Cast data to json in cache for better storage --- logs/app.log | 941 ++++++++++++++++++ .../java/org/pkwmtt/cache/CacheConfig.java | 6 +- .../java/org/pkwmtt/config/StartupConfig.java | 1 - .../GlobalExceptionHandler.java | 8 + .../timetable/TimetableCacheService.java | 98 +- .../pkwmtt/timetable/TimetableService.java | 1 - src/main/resources/application.properties | 6 +- .../org/pkwmtt/cache/CacheConfigTest.java | 13 +- 8 files changed, 1024 insertions(+), 50 deletions(-) diff --git a/logs/app.log b/logs/app.log index 84af5af..8b42ab2 100644 --- a/logs/app.log +++ b/logs/app.log @@ -1,2 +1,943 @@ 2025-08-04 21:26:19 ERROR org.pkwmtt.config.StartupConfig - TEST 2025-08-06 14:57:49 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 18:48:21 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 18:48:36 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class org.pkwmtt.cache.JsonCaffeineCache cannot be cast to class org.springframework.cache.caffeine.CaffeineCache (org.pkwmtt.cache.JsonCaffeineCache is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @523f58da; org.springframework.cache.caffeine.CaffeineCache is in unnamed module of loader 'app')] with root cause +java.lang.ClassCastException: class org.pkwmtt.cache.JsonCaffeineCache cannot be cast to class org.springframework.cache.caffeine.CaffeineCache (org.pkwmtt.cache.JsonCaffeineCache is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @523f58da; org.springframework.cache.caffeine.CaffeineCache is in unnamed module of loader 'app') + at org.pkwmtt.cache.CacheInspector.getAllEntries(CacheInspector.java:21) + at org.pkwmtt.cache.CacheInspector.printAllEntries(CacheInspector.java:36) + at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:40) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-08 18:53:47 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 18:54:25 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 18:55:03 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 18:55:41 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:03:05 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7b6faf7a] failed + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService + at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) + at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) + ... 23 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + ... 27 common frames omitted +2025-08-08 19:07:27 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@63d2637c] failed + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService + at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) + at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) + ... 23 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + ... 27 common frames omitted +2025-08-08 19:09:04 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@4f146f09] failed + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService + at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) + at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) + ... 23 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + ... 27 common frames omitted +2025-08-08 19:25:46 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:27:29 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:43:32 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:48:10 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:49:58 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:51:51 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:52:29 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:55:07 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:55:44 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:56:26 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7066de7d] failed + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService + at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) + at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) + ... 23 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + ... 27 common frames omitted +2025-08-08 19:59:00 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:59:40 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 19:59:51 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause +java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) + at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) + at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) + at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-08 20:00:14 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause +java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) + at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) + at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) + at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-08 20:00:19 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause +java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) + at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) + at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) + at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-08 20:00:20 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause +java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) + at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) + at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) + at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-08 20:02:52 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@10f904e1] failed + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService + at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) + at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) + at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) + ... 23 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + ... 27 common frames omitted +2025-08-08 20:06:32 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:15:51 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:16:29 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:28:12 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:29:02 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:31:54 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:31:57 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:32:47 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:32:50 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:33:55 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:33:57 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:34:21 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:34:23 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:34:41 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:34:44 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:35:04 ERROR org.pkwmtt.config.StartupConfig - TEST +2025-08-08 20:35:07 ERROR org.pkwmtt.config.StartupConfig - TEST diff --git a/src/main/java/org/pkwmtt/cache/CacheConfig.java b/src/main/java/org/pkwmtt/cache/CacheConfig.java index 97036fe..806a692 100644 --- a/src/main/java/org/pkwmtt/cache/CacheConfig.java +++ b/src/main/java/org/pkwmtt/cache/CacheConfig.java @@ -12,17 +12,19 @@ @Configuration @EnableCaching public class CacheConfig { + @Bean public Caffeine caffeineConfig() { return Caffeine.newBuilder() - .expireAfterWrite(24, TimeUnit.HOURS) + .expireAfterWrite(12, TimeUnit.HOURS) .recordStats(); } @Bean public CacheManager cacheManager(Caffeine caffeine) { - CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + CaffeineCacheManager cacheManager = new CaffeineCacheManager("timetables"); cacheManager.setCaffeine(caffeine); return cacheManager; } + } diff --git a/src/main/java/org/pkwmtt/config/StartupConfig.java b/src/main/java/org/pkwmtt/config/StartupConfig.java index aa8ce3a..234dee7 100644 --- a/src/main/java/org/pkwmtt/config/StartupConfig.java +++ b/src/main/java/org/pkwmtt/config/StartupConfig.java @@ -23,7 +23,6 @@ public class StartupConfig { @EventListener(ContextRefreshedEvent.class) public void onApplicationEvent() { - log.error("TEST"); try { if (port.isEmpty() || address.isEmpty()) throw new Exception(); diff --git a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java index 673b7ca..3089e05 100644 --- a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java +++ b/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +@SuppressWarnings({"LoggingSimilarMessage", "StringConcatenationArgumentToLogCall"}) @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @@ -34,4 +35,11 @@ public ResponseEntity handleJsonProcessingException(JsonProces public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsException(SpecifiedGeneralGroupDoesntExistsException e) { return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.BAD_REQUEST); } + + @ExceptionHandler(IllegalAccessException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity handleIllegalAccessException(IllegalAccessException e) { + log.error("INTERNAL_SERVER_ERROR # " + e.getMessage() + " # " + e.getCause()); + return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + } } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 2d73cb3..1e002de 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -1,14 +1,15 @@ package org.pkwmtt.timetable; -import lombok.RequiredArgsConstructor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.pkwmtt.timetable.dto.TimetableDTO; import org.pkwmtt.timetable.parser.TimetableParserService; -import org.springframework.cache.annotation.CacheConfig; -import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import java.io.IOException; @@ -16,10 +17,20 @@ import java.util.Map; @Service -@RequiredArgsConstructor -@CacheConfig(cacheNames = "timetables") public class TimetableCacheService { private final TimetableParserService parser; + private final ObjectMapper mapper; + + private final Cache cache; + + public TimetableCacheService(TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) throws + IllegalAccessException { + this.parser = parser; + this.mapper = mapper; + cache = cacheManager.getCache("timetables"); + + if (cache == null) throw new IllegalAccessException("Cache [timetables] not configured"); + } /** * Fetches and parses the full timetable for a general group. @@ -28,25 +39,24 @@ public class TimetableCacheService { * @return parsed timetable * @throws WebPageContentNotAvailableException if remote content is unavailable */ - @Cacheable(key = "#generalGroupName") - public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { + public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebPageContentNotAvailableException, + SpecifiedGeneralGroupDoesntExistsException { var generalGroupList = getGeneralGroupsList(); - if (!generalGroupList.containsKey(generalGroupName)){ - throw new SpecifiedGeneralGroupDoesntExistsException(); - } + if (!generalGroupList.containsKey(generalGroupName)) throw new SpecifiedGeneralGroupDoesntExistsException(); - Document document; - String url = generalGroupList.get(generalGroupName); - try { - document = Jsoup - .connect(String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", url)) - .get(); - } catch (IOException ioe) { - throw new WebPageContentNotAvailableException(); - } + String groupUrl = generalGroupList.get(generalGroupName); + String url = String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", groupUrl); + String cacheKey = "timetable_" + generalGroupName; + String json = cache.get( + cacheKey, + () -> mapper.writeValueAsString(new TimetableDTO(generalGroupName, parser.parse(fetchData(url)))) + ); - return new TimetableDTO(generalGroupName, parser.parse(document.html())); + return getMappedValue( + json, cacheKey, cache, new TypeReference<>() { + } + ); } /** @@ -55,18 +65,17 @@ public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebP * @return map of group names to URLs * @throws WebPageContentNotAvailableException if the source page can't be fetched */ - @Cacheable(key = "'generalGroupList'") public Map getGeneralGroupsList() throws WebPageContentNotAvailableException { - Document document; - try { - document = Jsoup - .connect("http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html") - .get(); - } catch (IOException ioe) { - throw new WebPageContentNotAvailableException(); - } + String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; + String json = cache.get( + "generalGroupList", + () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) + ); + return getMappedValue( + json, "generalGroupList", cache, new TypeReference<>() { + } + ); - return parser.parseGeneralGroups(document.html()); } @@ -76,16 +85,33 @@ public Map getGeneralGroupsList() throws WebPageContentNotAvaila * @return list of hour labels (e.g., 08:00–09:30) * @throws WebPageContentNotAvailableException if hour definition page can't be loaded */ - @Cacheable(key = "'hoursList'") public List getListOfHours() throws WebPageContentNotAvailableException { + String url = "https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html"; + + String json = cache.get("hourList", () -> mapper.writeValueAsString(parser.parseHours(fetchData(url)))); + + return getMappedValue( + json, "hourList", cache, new TypeReference<>() { + } + ); + } + + private T getMappedValue(String json, String key, Cache cache, TypeReference targetClass) throws + WebPageContentNotAvailableException { try { - Document document = Jsoup - .connect("https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html") - .get(); + return mapper.readValue(json, targetClass); + } catch (JsonProcessingException e) { + cache.evict(key); + throw new WebPageContentNotAvailableException(); + } + } - return parser.parseHours(document.html()); + private String fetchData(String url) throws WebPageContentNotAvailableException { + try { + return Jsoup.connect(url).get().html(); } catch (IOException ioe) { throw new WebPageContentNotAvailableException(); } } + } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index ee1951f..78cd3b3 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -72,7 +72,6 @@ public TimetableDTO getFilteredGeneralGroupSchedule(String generalGroupName, Lis for (var day : schedule) sub.forEach(day::filterByGroup); - System.out.println(); schedule.forEach(DayOfWeekDTO::deleteSubjectTypesFromNames); return new TimetableDTO(generalGroupName, schedule); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 911660b..6b9064b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -12,4 +12,8 @@ spring.jpa.hibernate.ddl-auto=none spring.datasource.hikari.initialization-fail-timeout=0 logging.file.name=logs/app.log -logging.file.path=logs \ No newline at end of file +logging.file.path=logs + +spring.cache.type=caffeine + +cache.name=PKWMTT \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java index fdad1ca..93ec81e 100644 --- a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java +++ b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java @@ -29,12 +29,10 @@ void testCacheKeyPresent_Schedule() { Cache cache = cacheManager.getCache("timetables"); assertThat(cache).isNotNull(); - Cache.ValueWrapper wrapper = cache.get("12K1"); + Cache.ValueWrapper wrapper = cache.get("timetable_12K1"); assertThat(wrapper).isNotNull(); - assertThat(wrapper.get()).isInstanceOf(TimetableDTO.class); + assertThat(wrapper.get()).isInstanceOf(String.class); - TimetableDTO second = service.getGeneralGroupSchedule("12K1"); - assertThat(second).isSameAs(wrapper.get()); } @Test @@ -44,12 +42,9 @@ void testCacheKeyPresent_HoursList(){ Cache cache = cacheManager.getCache("timetables"); assertThat(cache).isNotNull(); - Cache.ValueWrapper wrapper = cache.get("hoursList"); + Cache.ValueWrapper wrapper = cache.get("hourList"); assertThat(wrapper).isNotNull(); - assertThat(wrapper.get()).isInstanceOf(ArrayList.class); - - List second = service.getListOfHours(); - assertThat(second).isSameAs(wrapper.get()); + assertThat(wrapper.get()).isInstanceOf(String.class); } } \ No newline at end of file From b05eb7b0e0093b64f3e91d3441c6e5d7f59f9278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:39:26 +0200 Subject: [PATCH 10/37] Fix: Cast data to json in cache for better storage --- src/test/java/org/pkwmtt/cache/CacheConfigTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java index 93ec81e..bd1a630 100644 --- a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java +++ b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java @@ -2,15 +2,11 @@ import org.junit.jupiter.api.Test; import org.pkwmtt.timetable.TimetableCacheService; -import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import java.util.ArrayList; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; From 4858620f390c8e8232e10ecc030cdd2b6e705e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:39:40 +0200 Subject: [PATCH 11/37] Fix: Cast data to json in cache for better storage --- src/main/resources/application.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6b9064b..cd609c7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -15,5 +15,3 @@ logging.file.name=logs/app.log logging.file.path=logs spring.cache.type=caffeine - -cache.name=PKWMTT \ No newline at end of file From 0e73756310fdb40c064134256cef69d930569fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 11 Aug 2025 22:04:31 +0200 Subject: [PATCH 12/37] Make exception handler specifically for TimetableController; Change log message; --- .../TimetableExceptionHandler.java} | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) rename src/main/java/org/pkwmtt/{controllerAdvice/GlobalExceptionHandler.java => timetable/TimetableExceptionHandler.java} (85%) diff --git a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java similarity index 85% rename from src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java rename to src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java index 3089e05..09e6ac4 100644 --- a/src/main/java/org/pkwmtt/controllerAdvice/GlobalExceptionHandler.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java @@ -1,4 +1,4 @@ -package org.pkwmtt.controllerAdvice; +package org.pkwmtt.timetable; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; @@ -13,20 +13,19 @@ @SuppressWarnings({"LoggingSimilarMessage", "StringConcatenationArgumentToLogCall"}) @Slf4j -@RestControllerAdvice -public class GlobalExceptionHandler { - +@RestControllerAdvice(assignableTypes = {TimetableController.class}) +public class TimetableExceptionHandler { @ExceptionHandler(WebPageContentNotAvailableException.class) @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) public ResponseEntity handleWebPageContentNotAvailableException(WebPageContentNotAvailableException e) { - log.error("SERVICE_UNAVAILABLE # " + e.getMessage() + " # " + e.getCause()); + log.error("SERVICE_UNAVAILABLE # " + e.getMessage()); return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); } @ExceptionHandler(JsonProcessingException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseEntity handleJsonProcessingException(JsonProcessingException e) { - log.error("INTERNAL_SERVER_ERROR # " + e.getMessage() + " # " + e.getCause()); + log.error("INTERNAL_SERVER_ERROR # " + e.getMessage()); return new ResponseEntity<>(new ErrorResponseDTO("Json Processing Failed"), HttpStatus.INTERNAL_SERVER_ERROR); } @@ -39,7 +38,7 @@ public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsE @ExceptionHandler(IllegalAccessException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseEntity handleIllegalAccessException(IllegalAccessException e) { - log.error("INTERNAL_SERVER_ERROR # " + e.getMessage() + " # " + e.getCause()); + log.error("INTERNAL_SERVER_ERROR # " + e.getMessage()); return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); } } From 643ee3de96020cd4e8cc559b7be3512c9efb3ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 11 Aug 2025 22:05:02 +0200 Subject: [PATCH 13/37] Delete CacheInspectorController.java --- .../pkwmtt/temp/CacheInspectorController.java | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 src/main/java/org/pkwmtt/temp/CacheInspectorController.java diff --git a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java deleted file mode 100644 index e8470f2..0000000 --- a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.pkwmtt.temp; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.RequiredArgsConstructor; -import org.pkwmtt.cache.CacheInspector; -import org.pkwmtt.timetable.TimetableCacheService; -import org.pkwmtt.timetable.TimetableService; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -@RestController -@RequestMapping("/cache") -@RequiredArgsConstructor -public class CacheInspectorController { - private final TimetableService service; - private final TimetableCacheService cacheableTimetableService; - private final CacheInspector cacheInspector; - - @GetMapping - public String temp() { - List generalGroups = cacheableTimetableService.getGeneralGroupsList().keySet().stream().toList(); - StringBuilder stringBuilder = new StringBuilder(); - - generalGroups.forEach(group -> { - try { - stringBuilder.append(group).append(": "); - stringBuilder.append(service.getAvailableSubGroups(group).isEmpty() ? "\t\tBAD" : "\tGOOD"); - stringBuilder.append("\n"); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - - return String.format( - "%s\n\n%s", - stringBuilder, - cacheInspector.printAllEntries("timetables") - ); - } -} From 2bed0c13fe7d1130dab507bdf4959741a29cbed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 11 Aug 2025 22:14:03 +0200 Subject: [PATCH 14/37] Move ErrorResponseDTO to /exceptions/dto --- .../java/org/pkwmtt/exceptions/{ => dto}/ErrorResponseDTO.java | 2 +- .../java/org/pkwmtt/timetable/TimetableExceptionHandler.java | 2 +- src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/org/pkwmtt/exceptions/{ => dto}/ErrorResponseDTO.java (93%) diff --git a/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java b/src/main/java/org/pkwmtt/exceptions/dto/ErrorResponseDTO.java similarity index 93% rename from src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java rename to src/main/java/org/pkwmtt/exceptions/dto/ErrorResponseDTO.java index 0f44865..11c4e38 100644 --- a/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java +++ b/src/main/java/org/pkwmtt/exceptions/dto/ErrorResponseDTO.java @@ -1,4 +1,4 @@ -package org.pkwmtt.exceptions; +package org.pkwmtt.exceptions.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java index 09e6ac4..9312ff0 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; -import org.pkwmtt.exceptions.ErrorResponseDTO; +import org.pkwmtt.exceptions.dto.ErrorResponseDTO; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.springframework.http.HttpStatus; diff --git a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java index 42339bf..6025417 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; -import org.pkwmtt.exceptions.ErrorResponseDTO; +import org.pkwmtt.exceptions.dto.ErrorResponseDTO; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; From 7409d445c3f5dbdb9eb282124c284f1462e2c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 11 Aug 2025 22:14:17 +0200 Subject: [PATCH 15/37] Clear log files --- logs/app.log | 943 --------------------------------------------------- 1 file changed, 943 deletions(-) diff --git a/logs/app.log b/logs/app.log index 8b42ab2..e69de29 100644 --- a/logs/app.log +++ b/logs/app.log @@ -1,943 +0,0 @@ -2025-08-04 21:26:19 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-06 14:57:49 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 18:48:21 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 18:48:36 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class org.pkwmtt.cache.JsonCaffeineCache cannot be cast to class org.springframework.cache.caffeine.CaffeineCache (org.pkwmtt.cache.JsonCaffeineCache is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @523f58da; org.springframework.cache.caffeine.CaffeineCache is in unnamed module of loader 'app')] with root cause -java.lang.ClassCastException: class org.pkwmtt.cache.JsonCaffeineCache cannot be cast to class org.springframework.cache.caffeine.CaffeineCache (org.pkwmtt.cache.JsonCaffeineCache is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @523f58da; org.springframework.cache.caffeine.CaffeineCache is in unnamed module of loader 'app') - at org.pkwmtt.cache.CacheInspector.getAllEntries(CacheInspector.java:21) - at org.pkwmtt.cache.CacheInspector.printAllEntries(CacheInspector.java:36) - at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:40) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-08 18:53:47 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 18:54:25 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 18:55:03 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 18:55:41 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:03:05 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7b6faf7a] failed - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService - at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) - at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) - at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) - ... 23 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - ... 27 common frames omitted -2025-08-08 19:07:27 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@63d2637c] failed - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService - at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) - at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) - at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) - ... 23 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - ... 27 common frames omitted -2025-08-08 19:09:04 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@4f146f09] failed - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService - at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) - at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) - at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) - ... 23 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - ... 27 common frames omitted -2025-08-08 19:25:46 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:27:29 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:43:32 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:48:10 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:49:58 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:51:51 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:52:29 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:55:07 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:55:44 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:56:26 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@7066de7d] failed - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService - at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) - at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) - at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) - ... 23 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - ... 27 common frames omitted -2025-08-08 19:59:00 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:59:40 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 19:59:51 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause -java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) - at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) - at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) - at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) - at java.base/java.lang.Iterable.forEach(Iterable.java:75) - at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-08 20:00:14 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause -java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) - at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) - at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) - at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) - at java.base/java.lang.Iterable.forEach(Iterable.java:75) - at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-08 20:00:19 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause -java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) - at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) - at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) - at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) - at java.base/java.lang.Iterable.forEach(Iterable.java:75) - at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-08 20:00:20 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23)] with root cause -java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.pkwmtt.timetable.dto.TimetableDTO (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.pkwmtt.timetable.dto.TimetableDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @692dca23) - at org.pkwmtt.timetable.TimetableCacheService.getGeneralGroupSchedule(TimetableCacheService.java:67) - at org.pkwmtt.timetable.TimetableService.getAvailableSubGroups(TimetableService.java:34) - at org.pkwmtt.temp.CacheInspectorController.lambda$temp$0(CacheInspectorController.java:30) - at java.base/java.lang.Iterable.forEach(Iterable.java:75) - at org.pkwmtt.temp.CacheInspectorController.temp(CacheInspectorController.java:27) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-08 20:02:52 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheInspector': Resolution of declared constructors on bean Class [org.pkwmtt.cache.CacheInspector] from ClassLoader [org.springframework.boot.devtools.restart.classloader.RestartClassLoader@10f904e1] failed - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:384) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1334) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1229) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1222) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1188) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1123) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.NoClassDefFoundError: org/pkwmtt/timetable/TimetableCacheService - at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) - at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549) - at java.base/java.lang.Class.getDeclaredConstructors(Class.java:2727) - at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:379) - ... 23 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableCacheService - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - ... 27 common frames omitted -2025-08-08 20:06:32 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:15:51 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:16:29 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:28:12 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:29:02 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:31:54 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:31:57 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:32:47 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:32:50 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:33:55 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:33:57 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:34:21 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:34:23 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:34:41 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:34:44 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:35:04 ERROR org.pkwmtt.config.StartupConfig - TEST -2025-08-08 20:35:07 ERROR org.pkwmtt.config.StartupConfig - TEST From 3b814e7303f319257d4ccc7552ef9bd8405066a6 Mon Sep 17 00:00:00 2001 From: Krecik Date: Mon, 11 Aug 2025 22:17:00 +0200 Subject: [PATCH 16/37] Small improvements 1/X --- .../java/org/pkwmtt/cache/CacheInspector.java | 2 +- src/main/java/org/pkwmtt/entity/Exam.java | 15 ++++++------- src/main/java/org/pkwmtt/entity/ExamType.java | 15 ++++--------- .../java/org/pkwmtt/entity/GeneralGroup.java | 17 +++++--------- src/main/java/org/pkwmtt/entity/Group.java | 21 +++++++----------- src/main/java/org/pkwmtt/entity/OTPCode.java | 15 ++++--------- src/main/java/org/pkwmtt/entity/User.java | 22 +++++++------------ .../pkwmtt/exceptions/ErrorResponseDTO.java | 13 +++-------- ...fiedGeneralGroupDoesntExistsException.java | 2 -- .../WebPageContentNotAvailableException.java | 4 ---- .../pkwmtt/temp/CacheInspectorController.java | 2 +- .../timetable/TimetableCacheService.java | 21 ++++++++++++++---- .../pkwmtt/timetable/TimetableController.java | 10 ++++----- .../pkwmtt/timetable/dto/DayOfWeekDTO.java | 16 ++++++-------- .../org/pkwmtt/timetable/dto/SubjectDTO.java | 7 +++--- .../parser/TimetableParserService.java | 11 +++++----- .../CacheableTimetableServiceTest.java | 2 +- 17 files changed, 79 insertions(+), 116 deletions(-) diff --git a/src/main/java/org/pkwmtt/cache/CacheInspector.java b/src/main/java/org/pkwmtt/cache/CacheInspector.java index 7e92848..9003850 100644 --- a/src/main/java/org/pkwmtt/cache/CacheInspector.java +++ b/src/main/java/org/pkwmtt/cache/CacheInspector.java @@ -31,7 +31,7 @@ public Map getAllEntries(String cacheName) { public String printAllEntries(String cacheName) { service.getListOfHours(); service.getGeneralGroupSchedule("12K1"); - service.getGeneralGroupsList(); + service.getGeneralGroupsMap(); var s = new StringBuilder(); getAllEntries(cacheName).forEach((key, value) -> s diff --git a/src/main/java/org/pkwmtt/entity/Exam.java b/src/main/java/org/pkwmtt/entity/Exam.java index 68d376c..56f6fd8 100644 --- a/src/main/java/org/pkwmtt/entity/Exam.java +++ b/src/main/java/org/pkwmtt/entity/Exam.java @@ -1,31 +1,30 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.*; +import lombok.Data; import java.util.Date; @Entity -@Getter -@Builder -@RequiredArgsConstructor @Table(name = "`exams`") -@AllArgsConstructor +@Data public class Exam { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer exam_id; + @Column(name = "exam_id") + private Integer examId; private String title; private String description; private Date date; + //private Instant date; Proposition to change @Column(name = "`groups`") - private String exam_group; + private String examGroup; @ManyToOne @JoinColumn(name = "exam_type_id") - private ExamType exam_type; + private ExamType examType; } diff --git a/src/main/java/org/pkwmtt/entity/ExamType.java b/src/main/java/org/pkwmtt/entity/ExamType.java index 9ec4a4f..5df84a4 100644 --- a/src/main/java/org/pkwmtt/entity/ExamType.java +++ b/src/main/java/org/pkwmtt/entity/ExamType.java @@ -1,23 +1,16 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; @Entity -@Getter -@Builder -@AllArgsConstructor +@Data @Table(name = "`exam_type`") public class ExamType { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer exam_type_id; + @Column(name = "exam_type_id") + private Integer examTypeId; private String name; - - public ExamType() { - - } } \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/entity/GeneralGroup.java b/src/main/java/org/pkwmtt/entity/GeneralGroup.java index f03ff42..2aa1d23 100644 --- a/src/main/java/org/pkwmtt/entity/GeneralGroup.java +++ b/src/main/java/org/pkwmtt/entity/GeneralGroup.java @@ -1,28 +1,21 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; import java.util.Set; @Entity -@Getter -@Builder -@AllArgsConstructor +@Data @Table(name = "`general_group`") public class GeneralGroup { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer general_group_id; + @Column(name = "general_group_id") + private Integer generalGroupId; private String name; - @OneToMany(mappedBy = "general_group") + @OneToMany(mappedBy = "generalGroup") private Set groups; - - public GeneralGroup() { - - } } diff --git a/src/main/java/org/pkwmtt/entity/Group.java b/src/main/java/org/pkwmtt/entity/Group.java index cba5eaf..43ed486 100644 --- a/src/main/java/org/pkwmtt/entity/Group.java +++ b/src/main/java/org/pkwmtt/entity/Group.java @@ -1,29 +1,24 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; @Entity -@Getter -@Builder -@AllArgsConstructor +@Data @Table(name = "`groups`") +//Recommended change name of this class. Group is key word in sql and may lead to misunderstandings public class Group { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer group_id; + @Column(name = "group_id") + private Integer groupId; private String name; - private int group_count; + @Column(name = "group_count") + private int groupCount; @ManyToOne @JoinColumn(name = "general_group_id") - private GeneralGroup general_group; - - public Group() { - - } + private GeneralGroup generalGroup; } diff --git a/src/main/java/org/pkwmtt/entity/OTPCode.java b/src/main/java/org/pkwmtt/entity/OTPCode.java index 76d8110..6e8774d 100644 --- a/src/main/java/org/pkwmtt/entity/OTPCode.java +++ b/src/main/java/org/pkwmtt/entity/OTPCode.java @@ -1,21 +1,18 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; import java.time.LocalDateTime; @Entity -@Getter -@Builder -@AllArgsConstructor +@Data @Table(name = "otp_codes") public class OTPCode { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer otp_code_id; + @Column(name = "otp_code_id") + private Integer otpCodeId; private String code; @@ -26,8 +23,4 @@ public class OTPCode { @OneToOne @JoinColumn(name = "user_id", unique = true) private User user; - - public OTPCode() { - - } } diff --git a/src/main/java/org/pkwmtt/entity/User.java b/src/main/java/org/pkwmtt/entity/User.java index 448f135..9ea5060 100644 --- a/src/main/java/org/pkwmtt/entity/User.java +++ b/src/main/java/org/pkwmtt/entity/User.java @@ -1,35 +1,29 @@ package org.pkwmtt.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; import org.pkwmtt.enums.Role; @Entity -@Getter -@Builder -@AllArgsConstructor +@Data @Table(name = "`users`") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) - private Integer user_id; + @Column(name = "user_id") + private Integer userId; @ManyToOne @JoinColumn(name = "general_group_id") - private GeneralGroup general_group; + private GeneralGroup generalGroup; private String email; - private boolean is_active; + @Column(name = "is_active") + private boolean isActive; private Role role; @OneToOne(mappedBy = "user") - private OTPCode otp_code; - - public User() { - - } + private OTPCode otpCode; } diff --git a/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java b/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java index 0f44865..ed9e7f7 100644 --- a/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java +++ b/src/main/java/org/pkwmtt/exceptions/ErrorResponseDTO.java @@ -1,20 +1,13 @@ package org.pkwmtt.exceptions; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.time.LocalDateTime; -@AllArgsConstructor -@Builder -@Getter -@NoArgsConstructor +@Data public class ErrorResponseDTO { private String message; - @Builder.Default - private LocalDateTime timestamp = LocalDateTime.now(); + private LocalDateTime timestamp; public ErrorResponseDTO(String message) { this.message = message; diff --git a/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java b/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java index 7ab02ab..9e1bce7 100644 --- a/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java +++ b/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java @@ -1,7 +1,5 @@ package org.pkwmtt.exceptions; -import net.bytebuddy.asm.Advice; - public class SpecifiedGeneralGroupDoesntExistsException extends RuntimeException { public SpecifiedGeneralGroupDoesntExistsException() { super("Specified general group doesn't exists"); diff --git a/src/main/java/org/pkwmtt/exceptions/WebPageContentNotAvailableException.java b/src/main/java/org/pkwmtt/exceptions/WebPageContentNotAvailableException.java index 5733e6c..632308a 100644 --- a/src/main/java/org/pkwmtt/exceptions/WebPageContentNotAvailableException.java +++ b/src/main/java/org/pkwmtt/exceptions/WebPageContentNotAvailableException.java @@ -1,10 +1,6 @@ package org.pkwmtt.exceptions; public class WebPageContentNotAvailableException extends RuntimeException { - public WebPageContentNotAvailableException(String message) { - super(message); - } - public WebPageContentNotAvailableException() { super("Content of university webpage is not available right now."); } diff --git a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java index e8470f2..46e565e 100644 --- a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java +++ b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java @@ -21,7 +21,7 @@ public class CacheInspectorController { @GetMapping public String temp() { - List generalGroups = cacheableTimetableService.getGeneralGroupsList().keySet().stream().toList(); + List generalGroups = cacheableTimetableService.getGeneralGroupsMap().keySet().stream().toList(); StringBuilder stringBuilder = new StringBuilder(); generalGroups.forEach(group -> { diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 1e002de..2d545f0 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -15,6 +15,9 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.Objects.isNull; @Service public class TimetableCacheService { @@ -29,7 +32,9 @@ public TimetableCacheService(TimetableParserService parser, ObjectMapper mapper, this.mapper = mapper; cache = cacheManager.getCache("timetables"); - if (cache == null) throw new IllegalAccessException("Cache [timetables] not configured"); + if (isNull(cache)) { + throw new IllegalAccessException("Cache [timetables] not configured"); + } } /** @@ -41,9 +46,11 @@ public TimetableCacheService(TimetableParserService parser, ObjectMapper mapper, */ public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { - var generalGroupList = getGeneralGroupsList(); + var generalGroupList = getGeneralGroupsMap(); - if (!generalGroupList.containsKey(generalGroupName)) throw new SpecifiedGeneralGroupDoesntExistsException(); + if (!generalGroupList.containsKey(generalGroupName)) { + throw new SpecifiedGeneralGroupDoesntExistsException(); + } String groupUrl = generalGroupList.get(generalGroupName); String url = String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", groupUrl); @@ -65,7 +72,7 @@ public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebP * @return map of group names to URLs * @throws WebPageContentNotAvailableException if the source page can't be fetched */ - public Map getGeneralGroupsList() throws WebPageContentNotAvailableException { + public Map getGeneralGroupsMap() throws WebPageContentNotAvailableException { String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; String json = cache.get( "generalGroupList", @@ -78,6 +85,12 @@ public Map getGeneralGroupsList() throws WebPageContentNotAvaila } + public List getGeneralGroupsList() { + return getGeneralGroupsMap().keySet().stream() + .sorted() + .collect(Collectors.toList()); + } + /** * Retrieves the standard list of hour ranges used in the timetable. diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index fccdb6c..a8bbfe0 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -8,9 +8,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.Collections; import java.util.List; +import static java.util.Objects.isNull; + @RestController @RequestMapping("/pkmwtt/api/v1/timetables") @RequiredArgsConstructor @@ -29,8 +30,9 @@ public class TimetableController { @GetMapping("/{generalGroupName}") public ResponseEntity getGeneralGroupSchedule(@PathVariable String generalGroupName, @RequestParam(required = false) List sub) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { - if (sub == null || sub.isEmpty()) + if (isNull(sub) || sub.isEmpty()) { return ResponseEntity.ok(cacheableService.getGeneralGroupSchedule(generalGroupName)); + } //todo delete sub = sub.stream().map(String::toUpperCase).toList(); @@ -56,9 +58,7 @@ public ResponseEntity> getListOfHours() throws WebPageContentNotAva */ @GetMapping("/groups/general") public ResponseEntity> getListOfGeneralGroups() { - var result = new java.util.ArrayList<>(cacheableService.getGeneralGroupsList().keySet().stream().toList()); - Collections.sort(result); - return ResponseEntity.ok(result); + return ResponseEntity.ok(cacheableService.getGeneralGroupsList()); } /** diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index ea9bd2f..5ab6438 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -1,15 +1,13 @@ package org.pkwmtt.timetable.dto; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; import java.util.regex.Pattern; -@Setter -@Getter + +@Data public class DayOfWeekDTO { private final String name; private List odd; @@ -51,10 +49,10 @@ public void filterByGroup(String group) { group = group.substring(1); // Extract the group letter (e.g., "K" from "K03") - String groupName = Character.toString(group.charAt(0)); + var groupName = String.valueOf(group.charAt(0)); // Extract the subgroup digit (e.g., "3" from "K03") - String targetNumber = Character.toString(group.charAt(group.length() - 1)); + var targetNumber = String.valueOf(group.charAt(group.length() - 1)); // Apply the filter to both odd- and even-week lists odd = filter(odd, groupName, targetNumber); @@ -96,8 +94,8 @@ private List filter(List list, String groupName, String * @return true if no non-target subgroup codes are present */ private boolean hasOnlyTargetGroup(String element, String groupName, String targetNumber) { - Pattern pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); - Matcher matcher = pattern.matcher(element); + var pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); + var matcher = pattern.matcher(element); if (!matcher.find()) return true; diff --git a/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java b/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java index bb3f214..7b71ce3 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/SubjectDTO.java @@ -1,14 +1,13 @@ package org.pkwmtt.timetable.dto; import lombok.*; +import lombok.experimental.Accessors; import org.pkwmtt.enums.SubjectType; import java.util.regex.Pattern; -@Builder -@Getter -@Setter -@AllArgsConstructor +@Data +@Accessors(chain = true) public class SubjectDTO { private String name; private String classroom; diff --git a/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java b/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java index 9be7295..af32752 100644 --- a/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java +++ b/src/main/java/org/pkwmtt/timetable/parser/TimetableParserService.java @@ -132,12 +132,11 @@ private SubjectDTO buildSubject(String rawName, String rawClassroom, int rowId) String classroom = cleanClassroomName(rawClassroom); SubjectType type = extractSubjectTypeFromName(name); - return SubjectDTO.builder() - .name(name) - .classroom(classroom) - .rowId(rowId) - .type(type) - .build(); + return new SubjectDTO() + .setName(name) + .setClassroom(classroom) + .setRowId(rowId) + .setType(type); } /** diff --git a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java b/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java index 55f6586..c8fcef6 100644 --- a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java @@ -22,7 +22,7 @@ public void checkIfHoursListBodyIsNotNull() { @Test public void checkIfGeneralGroupListIsNotNull() { - var response = service.getGeneralGroupsList(); + var response = service.getGeneralGroupsMap(); assertNotNull(response); assertFalse(response.isEmpty()); } From 7545645e12abcdde20a4c7fa171fc07d0b9f701e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:19:43 +0200 Subject: [PATCH 17/37] Code improvement --- logs/app.log | 1337 +++++++++++++++++ ...fiedGeneralGroupDoesntExistsException.java | 4 + ...pecifiedSubGroupDoesntExistsException.java | 12 + .../pkwmtt/temp/CacheInspectorController.java | 43 - .../timetable/TimetableCacheService.java | 97 +- .../pkwmtt/timetable/TimetableController.java | 50 +- .../timetable/TimetableExceptionHandler.java | 32 +- .../pkwmtt/timetable/TimetableService.java | 77 +- .../pkwmtt/timetable/dto/DayOfWeekDTO.java | 16 +- .../org/pkwmtt/cache/CacheInspectorTest.java | 18 - .../timetable/TimetableControllerTest.java | 10 +- 11 files changed, 1521 insertions(+), 175 deletions(-) create mode 100644 src/main/java/org/pkwmtt/exceptions/SpecifiedSubGroupDoesntExistsException.java delete mode 100644 src/main/java/org/pkwmtt/temp/CacheInspectorController.java delete mode 100644 src/test/java/org/pkwmtt/cache/CacheInspectorTest.java diff --git a/logs/app.log b/logs/app.log index e69de29..c19acaf 100644 --- a/logs/app.log +++ b/logs/app.log @@ -0,0 +1,1337 @@ +2025-08-11 23:21:48 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-11 23:21:49 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-11 23:22:35 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-11 23:25:54 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-11 23:26:11 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:28:21 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:29:24 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:31:56 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:35:09 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:36:21 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause +java.lang.UnsupportedOperationException: null + at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) + at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) + at java.base/java.util.Collections.sort(Collections.java:145) + at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-11 23:41:23 ERROR o.s.b.d.LoggingFailureAnalysisReporter - + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Parameter 0 of constructor in org.pkwmtt.temp.CacheInspectorController required a bean of type 'org.pkwmtt.timetable.TimetableService' that could not be found. + + +Action: + +Consider defining a bean of type 'org.pkwmtt.timetable.TimetableService' in your configuration. + +2025-08-11 23:58:48 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException: Specified general group doesn't exists] with root cause +org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException: Specified general group doesn't exists + at org.pkwmtt.timetable.TimetableService.getFilteredGeneralGroupSchedule(TimetableService.java:84) + at org.pkwmtt.timetable.TimetableController.getGeneralGroupSchedule(TimetableController.java:39) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) + at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) + at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) + at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) + at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) + at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) + at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) + at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) + at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) + at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) + at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) + at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) + at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) + at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) + at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) + at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) + at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) + at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) + at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) + at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) + at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) + at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) + at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) + at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) + at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) + at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) + at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) + at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) + at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) + at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) + at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) + at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) + at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) + at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) + at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) + at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) + at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) + at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) + at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) + at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) + at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) + at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) + at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) + at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) + at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) + at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) + at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) + at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) + at java.base/java.lang.Thread.run(Thread.java:1583) +2025-08-12 00:14:42 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-12 00:14:43 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-12 00:14:58 ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) + at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) + at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) + at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) + at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) + at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) + at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) + at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) + at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) + at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) + ... 16 common frames omitted +Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:534) + at java.base/java.lang.Class.forName(Class.java:513) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) + ... 28 common frames omitted +2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:16:42 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. +2025-08-12 00:16:56 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. diff --git a/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java b/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java index 9e1bce7..f163b42 100644 --- a/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java +++ b/src/main/java/org/pkwmtt/exceptions/SpecifiedGeneralGroupDoesntExistsException.java @@ -4,4 +4,8 @@ public class SpecifiedGeneralGroupDoesntExistsException extends RuntimeException public SpecifiedGeneralGroupDoesntExistsException() { super("Specified general group doesn't exists"); } + + public SpecifiedGeneralGroupDoesntExistsException(String generalGroupName) { + super(String.format("Specified general group [%s] doesn't exists", generalGroupName)); + } } diff --git a/src/main/java/org/pkwmtt/exceptions/SpecifiedSubGroupDoesntExistsException.java b/src/main/java/org/pkwmtt/exceptions/SpecifiedSubGroupDoesntExistsException.java new file mode 100644 index 0000000..3fe095a --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/SpecifiedSubGroupDoesntExistsException.java @@ -0,0 +1,12 @@ +package org.pkwmtt.exceptions; + +public class SpecifiedSubGroupDoesntExistsException + extends RuntimeException { + public SpecifiedSubGroupDoesntExistsException () { + super("Specified sub group doesn't exists"); + } + + public SpecifiedSubGroupDoesntExistsException (String subgroupName) { + super(String.format("Specified sub group [%s] doesn't exists", subgroupName)); + } +} diff --git a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java b/src/main/java/org/pkwmtt/temp/CacheInspectorController.java deleted file mode 100644 index 46e565e..0000000 --- a/src/main/java/org/pkwmtt/temp/CacheInspectorController.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.pkwmtt.temp; - -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.RequiredArgsConstructor; -import org.pkwmtt.cache.CacheInspector; -import org.pkwmtt.timetable.TimetableCacheService; -import org.pkwmtt.timetable.TimetableService; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -@RestController -@RequestMapping("/cache") -@RequiredArgsConstructor -public class CacheInspectorController { - private final TimetableService service; - private final TimetableCacheService cacheableTimetableService; - private final CacheInspector cacheInspector; - - @GetMapping - public String temp() { - List generalGroups = cacheableTimetableService.getGeneralGroupsMap().keySet().stream().toList(); - StringBuilder stringBuilder = new StringBuilder(); - - generalGroups.forEach(group -> { - try { - stringBuilder.append(group).append(": "); - stringBuilder.append(service.getAvailableSubGroups(group).isEmpty() ? "\t\tBAD" : "\tGOOD"); - stringBuilder.append("\n"); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - - return String.format( - "%s\n\n%s", - stringBuilder, - cacheInspector.printAllEntries("timetables") - ); - } -} diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 2d545f0..eb1eb7d 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -15,28 +15,25 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; - -import static java.util.Objects.isNull; @Service public class TimetableCacheService { private final TimetableParserService parser; private final ObjectMapper mapper; - + private final Cache cache; - - public TimetableCacheService(TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) throws - IllegalAccessException { + + public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) + throws IllegalAccessException { this.parser = parser; this.mapper = mapper; cache = cacheManager.getCache("timetables"); - - if (isNull(cache)) { + + if (cache == null) { throw new IllegalAccessException("Cache [timetables] not configured"); } } - + /** * Fetches and parses the full timetable for a general group. * @@ -44,73 +41,77 @@ public TimetableCacheService(TimetableParserService parser, ObjectMapper mapper, * @return parsed timetable * @throws WebPageContentNotAvailableException if remote content is unavailable */ - public TimetableDTO getGeneralGroupSchedule(String generalGroupName) throws WebPageContentNotAvailableException, - SpecifiedGeneralGroupDoesntExistsException { + public TimetableDTO getGeneralGroupSchedule (String generalGroupName) + throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { var generalGroupList = getGeneralGroupsMap(); - + if (!generalGroupList.containsKey(generalGroupName)) { - throw new SpecifiedGeneralGroupDoesntExistsException(); + throw new SpecifiedGeneralGroupDoesntExistsException(generalGroupName); } - + String groupUrl = generalGroupList.get(generalGroupName); String url = String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", groupUrl); String cacheKey = "timetable_" + generalGroupName; String json = cache.get( - cacheKey, - () -> mapper.writeValueAsString(new TimetableDTO(generalGroupName, parser.parse(fetchData(url)))) + cacheKey, + () -> mapper.writeValueAsString(new TimetableDTO( + generalGroupName, + parser.parse(fetchData(url)) + )) ); - + return getMappedValue( - json, cacheKey, cache, new TypeReference<>() { - } + json, cacheKey, cache, new TypeReference<>() { + } ); } - + /** * Retrieves a mapping of general group names to their corresponding timetable URLs. * * @return map of group names to URLs * @throws WebPageContentNotAvailableException if the source page can't be fetched */ - public Map getGeneralGroupsMap() throws WebPageContentNotAvailableException { + public Map getGeneralGroupsMap () throws WebPageContentNotAvailableException { String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; String json = cache.get( - "generalGroupList", - () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) + "generalGroupList", + () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData( + url))) ); + return getMappedValue( - json, "generalGroupList", cache, new TypeReference<>() { - } + json, "generalGroupList", cache, new TypeReference<>() { + } ); - - } - - public List getGeneralGroupsList() { - return getGeneralGroupsMap().keySet().stream() - .sorted() - .collect(Collectors.toList()); } - - + /** * Retrieves the standard list of hour ranges used in the timetable. * * @return list of hour labels (e.g., 08:00–09:30) * @throws WebPageContentNotAvailableException if hour definition page can't be loaded */ - public List getListOfHours() throws WebPageContentNotAvailableException { + public List getListOfHours () throws WebPageContentNotAvailableException { String url = "https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html"; - - String json = cache.get("hourList", () -> mapper.writeValueAsString(parser.parseHours(fetchData(url)))); - - return getMappedValue( - json, "hourList", cache, new TypeReference<>() { - } + String json = cache.get( + "hourList", + () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) + ); + + List result = getMappedValue( + json, "hourList", cache, new TypeReference<>() { + } ); + + //Delete useless spaces + result = result.stream().map(item -> item.replaceAll(" ", "")).toList(); + + return result; } - - private T getMappedValue(String json, String key, Cache cache, TypeReference targetClass) throws - WebPageContentNotAvailableException { + + private T getMappedValue (String json, String key, Cache cache, TypeReference targetClass) + throws WebPageContentNotAvailableException { try { return mapper.readValue(json, targetClass); } catch (JsonProcessingException e) { @@ -118,13 +119,13 @@ private T getMappedValue(String json, String key, Cache cache, TypeReference throw new WebPageContentNotAvailableException(); } } - - private String fetchData(String url) throws WebPageContentNotAvailableException { + + private String fetchData (String url) throws WebPageContentNotAvailableException { try { return Jsoup.connect(url).get().html(); } catch (IOException ioe) { throw new WebPageContentNotAvailableException(); } } - + } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index a8bbfe0..c94158e 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.RequiredArgsConstructor; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; +import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.http.ResponseEntity; @@ -10,36 +11,35 @@ import java.util.List; -import static java.util.Objects.isNull; - @RestController @RequestMapping("/pkmwtt/api/v1/timetables") @RequiredArgsConstructor public class TimetableController { private final TimetableService service; private final TimetableCacheService cacheableService; - + /** * Provide schedule of specified group and filters if all provided * * @param generalGroupName name of general group - * @param sub list of subgroups + * @param subgroups list of subgroups * @return schedule of specified group with provided filters * @throws WebPageContentNotAvailableException . */ @GetMapping("/{generalGroupName}") - public ResponseEntity getGeneralGroupSchedule(@PathVariable String generalGroupName, @RequestParam(required = false) List sub) - throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { - if (isNull(sub) || sub.isEmpty()) { + public ResponseEntity getGeneralGroupSchedule (@PathVariable String generalGroupName, @RequestParam(required = false, name = "sub") List subgroups) + throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException, + SpecifiedSubGroupDoesntExistsException, JsonProcessingException { + + if (subgroups == null || subgroups.isEmpty()) { return ResponseEntity.ok(cacheableService.getGeneralGroupSchedule(generalGroupName)); } - - //todo delete - sub = sub.stream().map(String::toUpperCase).toList(); - - return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule(generalGroupName.toUpperCase(), sub)); + return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule( + generalGroupName.toUpperCase(), + subgroups + )); } - + /** * Provides list of schedule hours * @@ -47,20 +47,22 @@ public ResponseEntity getGeneralGroupSchedule(@PathVariable String * @throws WebPageContentNotAvailableException . */ @GetMapping("/hours") - public ResponseEntity> getListOfHours() throws WebPageContentNotAvailableException { + public ResponseEntity> getListOfHours () + throws WebPageContentNotAvailableException { return ResponseEntity.ok(cacheableService.getListOfHours()); } - + /** * Provides list of general groups * * @return list of general groups */ @GetMapping("/groups/general") - public ResponseEntity> getListOfGeneralGroups() { - return ResponseEntity.ok(cacheableService.getGeneralGroupsList()); + public ResponseEntity> getListOfGeneralGroups () + throws WebPageContentNotAvailableException { + return ResponseEntity.ok(service.getGeneralGroupList()); } - + /** * Provides list of available subgroups for specified general group * @@ -69,11 +71,11 @@ public ResponseEntity> getListOfGeneralGroups() { * @throws JsonProcessingException . */ @GetMapping("/groups/{generalGroupName}") - public ResponseEntity> getListOfAvailableGroups(@PathVariable String generalGroupName) - throws JsonProcessingException, SpecifiedGeneralGroupDoesntExistsException, WebPageContentNotAvailableException { - return ResponseEntity.ok(service.getAvailableSubGroups(generalGroupName.toUpperCase())); + public ResponseEntity> getListOfAvailableGroups (@PathVariable String generalGroupName) + throws JsonProcessingException, SpecifiedGeneralGroupDoesntExistsException, + WebPageContentNotAvailableException { + return ResponseEntity.ok(service.getAvailableSubGroups(generalGroupName)); } - - - + + } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java index 9312ff0..bb92055 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableExceptionHandler.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; +import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.pkwmtt.exceptions.dto.ErrorResponseDTO; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; @@ -17,28 +18,37 @@ public class TimetableExceptionHandler { @ExceptionHandler(WebPageContentNotAvailableException.class) @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) - public ResponseEntity handleWebPageContentNotAvailableException(WebPageContentNotAvailableException e) { + public ResponseEntity handleWebPageContentNotAvailableException (WebPageContentNotAvailableException e) { log.error("SERVICE_UNAVAILABLE # " + e.getMessage()); - return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); + return new ResponseEntity<>( + new ErrorResponseDTO(e.getMessage()), + HttpStatus.SERVICE_UNAVAILABLE + ); } - + @ExceptionHandler(JsonProcessingException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ResponseEntity handleJsonProcessingException(JsonProcessingException e) { + public ResponseEntity handleJsonProcessingException (JsonProcessingException e) { log.error("INTERNAL_SERVER_ERROR # " + e.getMessage()); - return new ResponseEntity<>(new ErrorResponseDTO("Json Processing Failed"), HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>( + new ErrorResponseDTO("Json Processing Failed"), + HttpStatus.INTERNAL_SERVER_ERROR + ); } - - @ExceptionHandler(SpecifiedGeneralGroupDoesntExistsException.class) + + @ExceptionHandler({SpecifiedGeneralGroupDoesntExistsException.class, SpecifiedSubGroupDoesntExistsException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsException(SpecifiedGeneralGroupDoesntExistsException e) { + public ResponseEntity handleSpecifiedGeneralGroupDoesntExistsException (Exception e) { return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.BAD_REQUEST); } - + @ExceptionHandler(IllegalAccessException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ResponseEntity handleIllegalAccessException(IllegalAccessException e) { + public ResponseEntity handleIllegalAccessException (IllegalAccessException e) { log.error("INTERNAL_SERVER_ERROR # " + e.getMessage()); - return new ResponseEntity<>(new ErrorResponseDTO(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>( + new ErrorResponseDTO(e.getMessage()), + HttpStatus.INTERNAL_SERVER_ERROR + ); } } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index 78cd3b3..c6f2238 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -5,7 +5,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; +import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; +import org.pkwmtt.repository.GroupRepository; import org.pkwmtt.timetable.dto.DayOfWeekDTO; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.stereotype.Service; @@ -19,7 +21,7 @@ @RequiredArgsConstructor public class TimetableService { private final TimetableCacheService cacheableTimetableService; - + /** * Parses the timetable JSON to extract subgroup identifiers like K01, P03, GL04 using regex. * @@ -27,37 +29,41 @@ public class TimetableService { * @return sorted list of subgroup names found in the timetable * @throws JsonProcessingException if timetable conversion to JSON fails */ - public List getAvailableSubGroups(String generalGroupName) - //TODO group name to upper case - throws JsonProcessingException, SpecifiedGeneralGroupDoesntExistsException, WebPageContentNotAvailableException { - ObjectMapper mapper = new ObjectMapper(); + public List getAvailableSubGroups (String generalGroupName) + throws JsonProcessingException, SpecifiedGeneralGroupDoesntExistsException, + WebPageContentNotAvailableException { + + generalGroupName = generalGroupName.toUpperCase(); TimetableDTO timetable = cacheableTimetableService.getGeneralGroupSchedule(generalGroupName); + + ObjectMapper mapper = new ObjectMapper(); String timeTableAsJson = mapper.writeValueAsString(timetable); - + // Regex pattern for group codes like K01, GP03, L04, etc. String regex = "\\bG?[KPL]0[0-9]\\b"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(timeTableAsJson); - + Set matchedGroups = new HashSet<>(); - + //Check if text starts with 'G' and delete it // to match frontend requirements String text; while (matcher.find()) { text = matcher.group(); - if (text.startsWith("G")) + if (text.startsWith("G")) { text = text.substring(1); + } matchedGroups.add(text); } - + List result = new ArrayList<>(matchedGroups.stream().toList()); Collections.sort(result); - + return result; } - - + + /** * Retrieves timetable and filters entries based on subgroups parameters * @@ -66,16 +72,45 @@ public List getAvailableSubGroups(String generalGroupName) * @return filtered timetable * @throws WebPageContentNotAvailableException if source data can't be retrieved */ - public TimetableDTO getFilteredGeneralGroupSchedule(String generalGroupName, List sub) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { - List schedule = cacheableTimetableService.getGeneralGroupSchedule(generalGroupName).getData(); - - for (var day : schedule) + public TimetableDTO getFilteredGeneralGroupSchedule (String generalGroupName, List sub) + throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException, + JsonProcessingException { + + generalGroupName = generalGroupName.toUpperCase(); + + //Check if specified subgroup is available for this generalGroup + var subgroups = getAvailableSubGroups(generalGroupName); + for (var group : sub) { + if (!subgroups.contains(group)) { + throw new SpecifiedSubGroupDoesntExistsException(group); + } + } + + List schedule = cacheableTimetableService + .getGeneralGroupSchedule(generalGroupName) + .getData(); + + + for (var day : schedule) { sub.forEach(day::filterByGroup); - + } + schedule.forEach(DayOfWeekDTO::deleteSubjectTypesFromNames); - + return new TimetableDTO(generalGroupName, schedule); } - - + + /** + * @return List of general group's names + */ + public List getGeneralGroupList () throws WebPageContentNotAvailableException { + var result = new ArrayList<>(cacheableTimetableService + .getGeneralGroupsMap() + .keySet() + .stream() + .toList()); + Collections.sort(result); + return result; + } + } diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index 5ab6438..ea9bd2f 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -1,13 +1,15 @@ package org.pkwmtt.timetable.dto; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; import java.util.regex.Pattern; - -@Data +@Setter +@Getter public class DayOfWeekDTO { private final String name; private List odd; @@ -49,10 +51,10 @@ public void filterByGroup(String group) { group = group.substring(1); // Extract the group letter (e.g., "K" from "K03") - var groupName = String.valueOf(group.charAt(0)); + String groupName = Character.toString(group.charAt(0)); // Extract the subgroup digit (e.g., "3" from "K03") - var targetNumber = String.valueOf(group.charAt(group.length() - 1)); + String targetNumber = Character.toString(group.charAt(group.length() - 1)); // Apply the filter to both odd- and even-week lists odd = filter(odd, groupName, targetNumber); @@ -94,8 +96,8 @@ private List filter(List list, String groupName, String * @return true if no non-target subgroup codes are present */ private boolean hasOnlyTargetGroup(String element, String groupName, String targetNumber) { - var pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); - var matcher = pattern.matcher(element); + Pattern pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); + Matcher matcher = pattern.matcher(element); if (!matcher.find()) return true; diff --git a/src/test/java/org/pkwmtt/cache/CacheInspectorTest.java b/src/test/java/org/pkwmtt/cache/CacheInspectorTest.java deleted file mode 100644 index c6fe172..0000000 --- a/src/test/java/org/pkwmtt/cache/CacheInspectorTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.pkwmtt.cache; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class CacheInspectorTest { - - @Autowired - private CacheInspector inspector; - - @Test - public void inspectCachedData_timetables(){ - inspector.printAllEntries("timetables"); - } - -} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java index 6025417..7910616 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java @@ -10,6 +10,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -63,11 +65,14 @@ public void testGetGeneralGroupScheduleFiltered_withoutParams() throws JsonProce public void shouldReturnListOfGeneralGroups() { String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/groups/general", port); - ResponseEntity response = restTemplate.getForEntity(url, String[].class); + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() { + } + ); assertNotNull(response.getBody()); assertEquals(HttpStatus.OK, response.getStatusCode()); - List result = Arrays.asList(response.getBody()); + List result = response.getBody(); assertNotNull(result); result.forEach(System.out::println); } @@ -90,7 +95,6 @@ public void textException_SpecifiedGeneralGroupDoesntExistsException() { assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); assertNotNull(response.getBody()); - assertThat(response.getBody().getMessage()).contains("Specified general group doesn't exists"); assertThat(response.getBody().getTimestamp()).isBefore(LocalDateTime.now()); } From 4398152be8fc8b293da8b8154e8b3542e3510013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:00:26 +0200 Subject: [PATCH 18/37] Update README.md --- README.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/README.md b/README.md index 383f2d9..1ea4873 100644 --- a/README.md +++ b/README.md @@ -21,24 +21,7 @@ docker pull ghcr.io/pkttteam/pkwmtt-backend:latest ``` -### 2. Create a `.env` file - -Create `.env` file in project - -Update values like: - -```dotenv -MYSQL_ROOT_PASSWORD=example -MYSQL_DATABASE=example_db -MYSQL_USER=username -MYSQL_PASSWORD=password - -SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/example_db -SPRING_DATASOURCE_USERNAME=username -SPRING_DATASOURCE_PASSWORD=password -``` - -### 3. Run +### 2. Run ```shell docker run -d --name [image_name] -p 8080:8080 ghcr.io/pkttteam/pkwmttt-backend:[PACKAGE_NUMBER] From bc5a9c81a30eb0106b62af101ff5e23add20fe59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:13:40 +0200 Subject: [PATCH 19/37] Write more tests and improve existing ones --- .../timetable/TimetableCacheService.java | 19 ++- .../pkwmtt/timetable/TimetableController.java | 6 +- .../pkwmtt/timetable/TimetableService.java | 9 +- .../java/org/pkwmtt/cache/CacheInspector.java | 39 +++-- .../CacheableTimetableServiceTest.java | 30 ---- .../timetable/TimetableCacheServiceTest.java | 105 +++++++++++++ .../timetable/TimetableControllerTest.java | 147 +++++++++++++----- .../timetable/TimetableServiceTest.java | 93 +++++++++++ 8 files changed, 345 insertions(+), 103 deletions(-) rename src/{main => test}/java/org/pkwmtt/cache/CacheInspector.java (67%) delete mode 100644 src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java create mode 100644 src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java create mode 100644 src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index eb1eb7d..195d674 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -75,9 +75,8 @@ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) public Map getGeneralGroupsMap () throws WebPageContentNotAvailableException { String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; String json = cache.get( - "generalGroupList", - () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData( - url))) + "generalGroupMap", + () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) ); return getMappedValue( @@ -110,6 +109,15 @@ public List getListOfHours () throws WebPageContentNotAvailableException return result; } + /** + * @param json - json representation of java object + * @param key - cache key + * @param cache - cache object + * @param targetClass - type to map value to + * @param type of object + * @return java object type + * @throws WebPageContentNotAvailableException if there were trouble with fetching data + */ private T getMappedValue (String json, String key, Cache cache, TypeReference targetClass) throws WebPageContentNotAvailableException { try { @@ -120,6 +128,11 @@ private T getMappedValue (String json, String key, Cache cache, TypeReferenc } } + /** + * @param url - url of webpage + * @return html code of selected webpage + * @throws WebPageContentNotAvailableException if there were trouble with fetching data + */ private String fetchData (String url) throws WebPageContentNotAvailableException { try { return Jsoup.connect(url).get().html(); diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index c94158e..6031f86 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class TimetableController { private final TimetableService service; - private final TimetableCacheService cacheableService; + private final TimetableCacheService cachedService; /** * Provide schedule of specified group and filters if all provided @@ -32,7 +32,7 @@ public ResponseEntity getGeneralGroupSchedule (@PathVariable Strin SpecifiedSubGroupDoesntExistsException, JsonProcessingException { if (subgroups == null || subgroups.isEmpty()) { - return ResponseEntity.ok(cacheableService.getGeneralGroupSchedule(generalGroupName)); + return ResponseEntity.ok(cachedService.getGeneralGroupSchedule(generalGroupName)); } return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule( generalGroupName.toUpperCase(), @@ -49,7 +49,7 @@ public ResponseEntity getGeneralGroupSchedule (@PathVariable Strin @GetMapping("/hours") public ResponseEntity> getListOfHours () throws WebPageContentNotAvailableException { - return ResponseEntity.ok(cacheableService.getListOfHours()); + return ResponseEntity.ok(cachedService.getListOfHours()); } /** diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index c6f2238..33ceadd 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -7,7 +7,6 @@ import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; -import org.pkwmtt.repository.GroupRepository; import org.pkwmtt.timetable.dto.DayOfWeekDTO; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.stereotype.Service; @@ -20,7 +19,7 @@ @Service @RequiredArgsConstructor public class TimetableService { - private final TimetableCacheService cacheableTimetableService; + private final TimetableCacheService cachedService; /** * Parses the timetable JSON to extract subgroup identifiers like K01, P03, GL04 using regex. @@ -34,7 +33,7 @@ public List getAvailableSubGroups (String generalGroupName) WebPageContentNotAvailableException { generalGroupName = generalGroupName.toUpperCase(); - TimetableDTO timetable = cacheableTimetableService.getGeneralGroupSchedule(generalGroupName); + TimetableDTO timetable = cachedService.getGeneralGroupSchedule(generalGroupName); ObjectMapper mapper = new ObjectMapper(); String timeTableAsJson = mapper.writeValueAsString(timetable); @@ -86,7 +85,7 @@ public TimetableDTO getFilteredGeneralGroupSchedule (String generalGroupName, Li } } - List schedule = cacheableTimetableService + List schedule = cachedService .getGeneralGroupSchedule(generalGroupName) .getData(); @@ -104,7 +103,7 @@ public TimetableDTO getFilteredGeneralGroupSchedule (String generalGroupName, Li * @return List of general group's names */ public List getGeneralGroupList () throws WebPageContentNotAvailableException { - var result = new ArrayList<>(cacheableTimetableService + var result = new ArrayList<>(cachedService .getGeneralGroupsMap() .keySet() .stream() diff --git a/src/main/java/org/pkwmtt/cache/CacheInspector.java b/src/test/java/org/pkwmtt/cache/CacheInspector.java similarity index 67% rename from src/main/java/org/pkwmtt/cache/CacheInspector.java rename to src/test/java/org/pkwmtt/cache/CacheInspector.java index 9003850..6aa6403 100644 --- a/src/main/java/org/pkwmtt/cache/CacheInspector.java +++ b/src/test/java/org/pkwmtt/cache/CacheInspector.java @@ -11,38 +11,37 @@ @Component @RequiredArgsConstructor +@SuppressWarnings("unused") public class CacheInspector { - + private final CacheManager cacheManager; private final TimetableCacheService service; - - - public Map getAllEntries(String cacheName) { + + public Map getAllEntries (String cacheName) { CaffeineCache springCache = (CaffeineCache) cacheManager.getCache(cacheName); - - if (springCache == null) + + if (springCache == null) { throw new IllegalArgumentException("No cache with name " + cacheName); - + } + Cache nativeCache = springCache.getNativeCache(); - + return nativeCache.asMap(); } - - public String printAllEntries(String cacheName) { + + public String printAllEntries (String cacheName) { service.getListOfHours(); service.getGeneralGroupSchedule("12K1"); service.getGeneralGroupsMap(); var s = new StringBuilder(); - getAllEntries(cacheName).forEach((key, value) -> - s - .append("Cache[") - .append(cacheName) - .append("] ") - .append(key) - .append(" -> ") - .append(value) - .append("\n") - ); + getAllEntries(cacheName).forEach((key, value) -> s + .append("Cache[") + .append(cacheName) + .append("] ") + .append(key) + .append(" -> ") + .append(value) + .append("\n")); return s.toString(); } } diff --git a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java b/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java deleted file mode 100644 index c8fcef6..0000000 --- a/src/test/java/org/pkwmtt/timetable/CacheableTimetableServiceTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.pkwmtt.timetable; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@SpringBootTest -class CacheableTimetableServiceTest { - @Autowired - TimetableCacheService service; - - @Test - public void checkIfHoursListBodyIsNotNull() { - var response = service.getListOfHours(); - - assertNotNull(response); - assertFalse(response.isEmpty()); - } - - @Test - public void checkIfGeneralGroupListIsNotNull() { - var response = service.getGeneralGroupsMap(); - assertNotNull(response); - assertFalse(response.isEmpty()); - } - -} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java b/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java new file mode 100644 index 0000000..b84ec4d --- /dev/null +++ b/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java @@ -0,0 +1,105 @@ +package org.pkwmtt.timetable; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.pkwmtt.cache.CacheInspector; +import org.pkwmtt.timetable.dto.TimetableDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@Slf4j +@SpringBootTest +class TimetableCacheServiceTest { + @Autowired + TimetableCacheService cachedService; + + @Autowired + TimetableService service; + + @Autowired + CacheInspector cacheInspector; + + + @Test + public void shouldHourListBePresentInCache () { + //given + String key = "hourList"; + cachedService.getListOfHours(); // call method to save data in cache + + //when + Map cacheData = cacheInspector.getAllEntries("timetables"); // get all keys saved in cache + + //then + assertNotNull(cacheData); + assertTrue(cacheData.containsKey(key)); + assertNotNull(cacheData.get(key)); + } + + + @Test + public void shouldReturnGeneralGroupsMap () { + //given + Map result = cachedService.getGeneralGroupsMap(); + + //when + + //then + assertNotNull(result); + assertFalse(result.isEmpty()); + } + + @Test + public void shouldGeneralGroupMapBePresentInCache () { + //given + String key = "generalGroupMap"; + cachedService.getGeneralGroupsMap(); // call method to save data in cache + + //when + Map cacheData = cacheInspector.getAllEntries("timetables"); // get all keys saved in cache + + //then + assertNotNull(cacheData); + assertTrue(cacheData.containsKey(key)); + assertNotNull(cacheData.get(key)); + } + + @Test + public void shouldReturnRandomGeneralGroupSchedule () { + //given + List generalGroupList = service.getGeneralGroupList(); + String generalGroupName = generalGroupList.get((int) (Math.random() * generalGroupList.size())); // get random general group + + //when + var result = cachedService.getGeneralGroupSchedule(generalGroupName); + + //then + assertNotNull(result); + assertInstanceOf(TimetableDTO.class, result); + } + + @Test + public void shouldRandomGeneralGroupScheduleBePresentInCache () { + //given + List generalGroupList = service.getGeneralGroupList(); + + String generalGroupName = generalGroupList.get((int) (Math.random() * generalGroupList.size())); // get random general group + String key = "timetable_" + generalGroupName; + + cachedService.getGeneralGroupSchedule(generalGroupName); // call method to save data in cache + + //when + Map cacheData = cacheInspector.getAllEntries("timetables"); // get all keys saved in cache + + //then + assertNotNull(cacheData); + assertTrue(cacheData.containsKey(key)); + assertInstanceOf(String.class, cacheData.get(key)); + } + + +} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java index 7910616..30e8396 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java @@ -1,7 +1,5 @@ package org.pkwmtt.timetable; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.pkwmtt.exceptions.dto.ErrorResponseDTO; @@ -18,84 +16,149 @@ import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; @Slf4j @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class TimetableControllerTest { - + @LocalServerPort private int port; - + @Autowired private TestRestTemplate restTemplate; - + @Test - public void testGetGeneralGroupScheduleFiltered_withOptionalParams() { - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1?sub=K01&sub=L01&sub=P01", port); - + public void testGetGeneralGroupScheduleFiltered_withOptionalParams () { + //given + String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1?sub=K01&sub=L01&sub=P01", + port + ); + + //when ResponseEntity response = restTemplate.getForEntity(url, TimetableDTO.class); - + + //then assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); assertEquals(5, response.getBody().getData().size()); assertEquals(12, response.getBody().getData().getFirst().getOdd().size()); assertEquals(6, response.getBody().getData().getFirst().getEven().size()); } - + @Test - public void testGetGeneralGroupScheduleFiltered_withoutParams() throws JsonProcessingException { + public void testGetGeneralGroupScheduleFiltered_withoutParams () { + //given String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1", port); - + + //when ResponseEntity response = restTemplate.getForEntity(url, TimetableDTO.class); - + + //then assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); - ObjectMapper mapper = new ObjectMapper(); - - var result = mapper.writeValueAsString(response.getBody()); - System.out.println(result); - + + assertEquals(5, response.getBody().getData().size()); // 5 days a week } - + @Test - public void shouldReturnListOfGeneralGroups() { - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/groups/general", port); - + public void shouldReturnListOfGeneralGroups () { + //given + String url = String.format( + "http://localhost:%s/pkmwtt/api/v1/timetables/groups/general", + port + ); + + //when ResponseEntity> response = restTemplate.exchange( - url, HttpMethod.GET, null, new ParameterizedTypeReference<>() { - } + url, HttpMethod.GET, null, new ParameterizedTypeReference<>() { + } ); + + //then assertNotNull(response.getBody()); assertEquals(HttpStatus.OK, response.getStatusCode()); - - List result = response.getBody(); - assertNotNull(result); - result.forEach(System.out::println); + assertFalse(response.getBody().isEmpty()); } - + @Test - public void shouldReturnListOfSubgroupsForGeneralGroup() { - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/groups/12K1", port); - + public void shouldReturnListOfSubgroupsForGeneralGroup () { + //given + String url = String.format( + "http://localhost:%s/pkmwtt/api/v1/timetables/groups/12K1", + port + ); + + //when ResponseEntity response = restTemplate.getForEntity(url, String[].class); - - System.out.println(Arrays.toString(response.getBody())); + + //then assertNotNull(response.getBody()); assertEquals(HttpStatus.OK, response.getStatusCode()); } - + @Test - public void textException_SpecifiedGeneralGroupDoesntExistsException() { - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/groups/XXXX", port); - ResponseEntity response = restTemplate.getForEntity(url, ErrorResponseDTO.class); - + public void shouldReturn_BadRequest_SpecifiedGeneralGroupDoesntExistsException () { + //given + String url = String.format( + "http://localhost:%s/pkmwtt/api/v1/timetables/groups/XXXX", + port + ); + + //when + ResponseEntity response = restTemplate.getForEntity( + url, + ErrorResponseDTO.class + ); + + //then assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); assertNotNull(response.getBody()); assertThat(response.getBody().getTimestamp()).isBefore(LocalDateTime.now()); } - + + @Test + public void shouldReturn_BadRequest_SpecifiedSubGroupDoesntExistsException () { + //given + String url = String.format( + "http://localhost:%s/pkmwtt/api/v1/timetables/12K1?sub=XXX", + port + ); + + //when + ResponseEntity response = restTemplate.getForEntity( + url, + ErrorResponseDTO.class + ); + + //then + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertNotNull(response.getBody()); + assertThat(response.getBody().getTimestamp()).isBefore(LocalDateTime.now()); + } + + @Test + public void shouldReturn_ListOfHours () { + //given + String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/hours", port); + + //when + ResponseEntity response = restTemplate.getForEntity(url, String[].class); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + + String regex = "\\b(?:[0-9]|1[0-9]|2[0-3]):[0-5][0-9]-(?:[0-9]|1[0-9]|2[0-3]):[0-5][0-9]\\b"; + Pattern pattern = Pattern.compile(regex); + Arrays.stream(response.getBody()).toList().forEach(item -> { + Matcher matcher = pattern.matcher(item); + if(!matcher.find()) fail("Wrong hour format"); + }); + } } \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java b/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java new file mode 100644 index 0000000..9dd84aa --- /dev/null +++ b/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java @@ -0,0 +1,93 @@ +package org.pkwmtt.timetable; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.Test; +import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; +import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +@SpringBootTest +class TimetableServiceTest { + + @Autowired + private TimetableService service; + + @Test + public void shouldReturnAvailableSubGroups () throws JsonProcessingException { + //given + String generalGroupName = "12K1"; + String regex = "^[A-Z]\\d{2}$"; + Pattern pattern = Pattern.compile(regex); + + //when + var result = service.getAvailableSubGroups(generalGroupName); + + //then + assertNotNull(result); + assertFalse(result.isEmpty()); + + result.forEach(item -> { + Matcher matcher = pattern.matcher(item); + if (!matcher.find()) { + fail("Wrong subgroup format"); + } + }); + + } + + + @Test + public void shouldThrow_SpecifiedGeneralGroupDoesntExistsException () { + //given + List subgroups = List.of("K01", "L01"); + String generalGroupName = "77Z3"; + //when + + //then + assertThrows( + SpecifiedGeneralGroupDoesntExistsException.class, () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) + ); + + } + + @Test + public void shouldThrow_SpecifiedSubGroupDoesntExistsException () { + //given + List subgroups = List.of("Z01", "XCD"); + String generalGroupName = "12K1"; + //when + + //then + assertThrows( + SpecifiedSubGroupDoesntExistsException.class, () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) + ); + + } + + + @Test + public void shouldReturnSortedGeneralGroupList () { + //given + List result = service.getGeneralGroupList(); + List originalResult = new ArrayList<>(result); + //when + Collections.sort(result); + + //then + assertEquals(originalResult, result); + assertNotNull(result); + assertFalse(result.isEmpty()); + + } + +} \ No newline at end of file From 16b213822f818255da21bdc384189f12d8f45c35 Mon Sep 17 00:00:00 2001 From: Krecik Date: Mon, 11 Aug 2025 22:17:00 +0200 Subject: [PATCH 20/37] Small improvements 1/X --- .../timetable/TimetableCacheService.java | 77 ++++++++++--------- .../pkwmtt/timetable/TimetableController.java | 18 +++-- .../pkwmtt/timetable/dto/DayOfWeekDTO.java | 16 ++-- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 195d674..e33f855 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -15,25 +15,28 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.Objects.isNull; @Service public class TimetableCacheService { private final TimetableParserService parser; private final ObjectMapper mapper; - + private final Cache cache; - + public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) - throws IllegalAccessException { + throws IllegalAccessException { this.parser = parser; this.mapper = mapper; cache = cacheManager.getCache("timetables"); - - if (cache == null) { + + if (isNull(cache)) { throw new IllegalAccessException("Cache [timetables] not configured"); } } - + /** * Fetches and parses the full timetable for a general group. * @@ -42,30 +45,29 @@ public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper * @throws WebPageContentNotAvailableException if remote content is unavailable */ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) - throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { + throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { var generalGroupList = getGeneralGroupsMap(); - + if (!generalGroupList.containsKey(generalGroupName)) { throw new SpecifiedGeneralGroupDoesntExistsException(generalGroupName); } - + String groupUrl = generalGroupList.get(generalGroupName); String url = String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", groupUrl); String cacheKey = "timetable_" + generalGroupName; String json = cache.get( - cacheKey, - () -> mapper.writeValueAsString(new TimetableDTO( - generalGroupName, - parser.parse(fetchData(url)) - )) + cacheKey, + () -> mapper.writeValueAsString(new TimetableDTO( + generalGroupName, + parser.parse(fetchData(url)) + )) ); - + return getMappedValue( - json, cacheKey, cache, new TypeReference<>() { - } + json, cacheKey, cache, new TypeReference<>() {} ); } - + /** * Retrieves a mapping of general group names to their corresponding timetable URLs. * @@ -75,16 +77,21 @@ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) public Map getGeneralGroupsMap () throws WebPageContentNotAvailableException { String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; String json = cache.get( - "generalGroupMap", - () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) + "generalGroupMap", + () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) ); - + return getMappedValue( - json, "generalGroupList", cache, new TypeReference<>() { - } + json, "generalGroupList", cache, new TypeReference<>() { + } ); } - + public List getGeneralGroupsList() { + return getGeneralGroupsMap().keySet().stream() + .sorted() + .collect(Collectors.toList()); + } + /** * Retrieves the standard list of hour ranges used in the timetable. * @@ -94,21 +101,21 @@ public Map getGeneralGroupsMap () throws WebPageContentNotAvaila public List getListOfHours () throws WebPageContentNotAvailableException { String url = "https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html"; String json = cache.get( - "hourList", - () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) + "hourList", + () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) ); - + List result = getMappedValue( - json, "hourList", cache, new TypeReference<>() { - } + json, "hourList", cache, new TypeReference<>() { + } ); - + //Delete useless spaces result = result.stream().map(item -> item.replaceAll(" ", "")).toList(); - + return result; } - + /** * @param json - json representation of java object * @param key - cache key @@ -119,7 +126,7 @@ public List getListOfHours () throws WebPageContentNotAvailableException * @throws WebPageContentNotAvailableException if there were trouble with fetching data */ private T getMappedValue (String json, String key, Cache cache, TypeReference targetClass) - throws WebPageContentNotAvailableException { + throws WebPageContentNotAvailableException { try { return mapper.readValue(json, targetClass); } catch (JsonProcessingException e) { @@ -127,7 +134,7 @@ private T getMappedValue (String json, String key, Cache cache, TypeReferenc throw new WebPageContentNotAvailableException(); } } - + /** * @param url - url of webpage * @return html code of selected webpage @@ -140,5 +147,5 @@ private String fetchData (String url) throws WebPageContentNotAvailableException throw new WebPageContentNotAvailableException(); } } - + } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index 6031f86..a37f66a 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -11,13 +11,15 @@ import java.util.List; +import static java.util.Objects.isNull; + @RestController @RequestMapping("/pkmwtt/api/v1/timetables") @RequiredArgsConstructor public class TimetableController { private final TimetableService service; private final TimetableCacheService cachedService; - + /** * Provide schedule of specified group and filters if all provided * @@ -30,8 +32,8 @@ public class TimetableController { public ResponseEntity getGeneralGroupSchedule (@PathVariable String generalGroupName, @RequestParam(required = false, name = "sub") List subgroups) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException, SpecifiedSubGroupDoesntExistsException, JsonProcessingException { - - if (subgroups == null || subgroups.isEmpty()) { + + if (isNull(subgroups) || subgroups.isEmpty()) { return ResponseEntity.ok(cachedService.getGeneralGroupSchedule(generalGroupName)); } return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule( @@ -39,7 +41,7 @@ public ResponseEntity getGeneralGroupSchedule (@PathVariable Strin subgroups )); } - + /** * Provides list of schedule hours * @@ -51,7 +53,7 @@ public ResponseEntity> getListOfHours () throws WebPageContentNotAvailableException { return ResponseEntity.ok(cachedService.getListOfHours()); } - + /** * Provides list of general groups * @@ -62,7 +64,7 @@ public ResponseEntity> getListOfGeneralGroups () throws WebPageContentNotAvailableException { return ResponseEntity.ok(service.getGeneralGroupList()); } - + /** * Provides list of available subgroups for specified general group * @@ -76,6 +78,6 @@ public ResponseEntity> getListOfAvailableGroups (@PathVariable Stri WebPageContentNotAvailableException { return ResponseEntity.ok(service.getAvailableSubGroups(generalGroupName)); } - - + + } diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index ea9bd2f..5ab6438 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -1,15 +1,13 @@ package org.pkwmtt.timetable.dto; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; import java.util.regex.Pattern; -@Setter -@Getter + +@Data public class DayOfWeekDTO { private final String name; private List odd; @@ -51,10 +49,10 @@ public void filterByGroup(String group) { group = group.substring(1); // Extract the group letter (e.g., "K" from "K03") - String groupName = Character.toString(group.charAt(0)); + var groupName = String.valueOf(group.charAt(0)); // Extract the subgroup digit (e.g., "3" from "K03") - String targetNumber = Character.toString(group.charAt(group.length() - 1)); + var targetNumber = String.valueOf(group.charAt(group.length() - 1)); // Apply the filter to both odd- and even-week lists odd = filter(odd, groupName, targetNumber); @@ -96,8 +94,8 @@ private List filter(List list, String groupName, String * @return true if no non-target subgroup codes are present */ private boolean hasOnlyTargetGroup(String element, String groupName, String targetNumber) { - Pattern pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); - Matcher matcher = pattern.matcher(element); + var pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); + var matcher = pattern.matcher(element); if (!matcher.find()) return true; From 2f36a7c46119bba032618df122fe5c360484035e Mon Sep 17 00:00:00 2001 From: Krecik Date: Tue, 12 Aug 2025 22:52:55 +0200 Subject: [PATCH 21/37] Add wiremock into tests --- pom.xml | 6 +- .../timetable/TimetableCacheService.java | 20 +- src/test/java/org/pkwmtt/ValuesForTest.java | 269 ++++++++++++++++++ .../org/pkwmtt/cache/CacheConfigTest.java | 53 +++- .../timetable/parser/ParserServiceTest.java | 39 ++- .../resources/application-test.properties | 1 + 6 files changed, 372 insertions(+), 16 deletions(-) create mode 100644 src/test/java/org/pkwmtt/ValuesForTest.java create mode 100644 src/test/resources/application-test.properties diff --git a/pom.xml b/pom.xml index f84ab2c..5b85e73 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,11 @@ logback-classic 1.5.13 - + + org.wiremock.integrations + wiremock-spring-boot + 3.10.0 + diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index e33f855..7babc6d 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -8,6 +8,7 @@ import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.pkwmtt.timetable.dto.TimetableDTO; import org.pkwmtt.timetable.parser.TimetableParserService; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; @@ -26,6 +27,9 @@ public class TimetableCacheService { private final Cache cache; + @Value("${main.url:https://podzial.mech.pk.edu.pl/stacjonarne/html/}") + private String mainUrl; + public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) throws IllegalAccessException { this.parser = parser; @@ -53,14 +57,17 @@ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) } String groupUrl = generalGroupList.get(generalGroupName); - String url = String.format("https://podzial.mech.pk.edu.pl/stacjonarne/html/%s", groupUrl); + String url = mainUrl + groupUrl; String cacheKey = "timetable_" + generalGroupName; + var html = fetchData(url); String json = cache.get( cacheKey, - () -> mapper.writeValueAsString(new TimetableDTO( + () -> { + var timetableDTO =new TimetableDTO( generalGroupName, - parser.parse(fetchData(url)) - )) + parser.parse(html)); + return mapper.writeValueAsString(timetableDTO); + } ); return getMappedValue( @@ -75,10 +82,11 @@ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) * @throws WebPageContentNotAvailableException if the source page can't be fetched */ public Map getGeneralGroupsMap () throws WebPageContentNotAvailableException { - String url = "http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html"; + var url = mainUrl + "lista.html"; + var html = fetchData(url); String json = cache.get( "generalGroupMap", - () -> mapper.writeValueAsString(parser.parseGeneralGroups(fetchData(url))) + () -> mapper.writeValueAsString(parser.parseGeneralGroups(html)) ); return getMappedValue( diff --git a/src/test/java/org/pkwmtt/ValuesForTest.java b/src/test/java/org/pkwmtt/ValuesForTest.java new file mode 100644 index 0000000..dd5e92b --- /dev/null +++ b/src/test/java/org/pkwmtt/ValuesForTest.java @@ -0,0 +1,269 @@ +package org.pkwmtt; + +public interface ValuesForTest { + + String timetableHTML = """ + + + + + + Plan lekcji oddziału - 12K1 + + + + + + + + + + +
12K1
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NrGodzPoniedziałekWtorekŚrodaCzwartekPiątek
17:30- 8:15PInterfUż W-(N) #PIU J207.1-n
Proj3D W-(P) #3-D J207.1-p
    
28:15- 9:00PInterfUż W-(N) #PIU J207.1-n
Proj3D W-(P) #3-D J207.1-p
  GodzPrzem-(P) #GdP C04-p 
39:15-10:00Mechatro P04-(n. #Mtr K227-n
Proj3D K04-(p. #3D J207.1-p
Proj3D K01-(n. Do J207.1-n
PSieciKP L02-(N. AP G110-n
PSieciKP L02-(P. PA G110-p
Mechatro L01-(P. S! K228-p
PInterUż K01-(p. _D J207.1-pBazDan K04-(n. #Bda G117-n
BazDan K04-(p. #bdA G117-p
PKM K01-(n. KB A227-n
WspInfPM P01-(p. CS A338-p
Inżopr W-(N) #IOP A123-n
Inżopr W-(P) #IOp A123-p
410:00-10:45Mechatro P04-(n. #Mtr K227-n
Proj3D K04-(p. #3D J207.1-p
Proj3D K01-(n. Do J207.1-n
PSieciKP L02-(N. AP G110-n
PSieciKP L02-(P. PA G110-p
Mechatro L01-(P. S! K228-p
PInterUż K01-(p. _D J207.1-pBazDan K04-(n. #Bda G117-n
BazDan K04-(p. #bdA G117-p
PKM K01-(n. KB A227-n
WspInfPM P01-(p. CS A338-p
Inżopr W-(N) #IOP A123-n
Inżopr W-(P) #IOp A123-p
511:00-11:45PAplikIn K04-(n. #pai J209-n
PInterUż K04-(p. #piu J207.1-p
Mechatro P01-(n. TJ K227-n
PAplikIn K01-(p. _W J209-p
PSieciKP L02-(N. AP G110-n
PSieciKP L02-(P. PA G110-p
 BazDan K04-(n. #Bda G117-n
BazDan K04-(p. #bdA G117-p
PKM W-(P) #PKM A124-p
611:45-12:30PAplikIn K04-(n. #pai J209-n
PInterUż K04-(p. #piu J207.1-p
Mechatro P01-(n. TJ K227-n
PAplikIn K01-(p. _W J209-p
PSieciKP L01-(N. AP G110-n
PSieciKP L01-(P. PA G110-p
Inżopr P01-(n. Gj G120-n
Inżopr K01-(p. gJ G120-p
 PKM W-(P) #PKM A124-p
712:45-13:30PrSteroP L01-(n. SP G107-n
PrSteroP L01-(p. sp G107-p
PSieciKP L01-(N. AP G110-n
PSieciKP L01-(P. PA G110-p
Mechatro L02-(N. K228-n
Inżopr P01-(n. Gj G120-n
Inżopr K01-(p. gJ G120-p
Inżopr P04-(n. #ioP G120-n
Inżopr K04-(p. #iop G120-p
WspInfPM W-(N) #WPM A124-n
Mechatron W-(P) #MTR G18-p
813:30-14:15PrSteroP L01-(n. SP G107-n
PrSteroP L01-(p. sp G107-p
PSieciKP L01-(N. AP G110-n
PSieciKP L01-(P. PA G110-p
Mechatro L02-(N. K228-n
Inżopr P01-(n. Gj G120-n
Inżopr K01-(p. gJ G120-p
Inżopr P04-(n. #ioP G120-n
Inżopr K04-(p. #iop G120-p
WspInfPM W-(N) #WPM A124-n
Mechatron W-(P) #MTR G18-p
914:30-15:15PKM K04-(N. #Pkm A227-nPPSystM K04-(n. #Psm J209-n
PPSystM K04-(p. #PSm J209-p
 WspInfPM P04-(N) #Wpm A338-n
PSieciKP W-(P) #PKP A437-p
BazDan W-(N) #BDa G18-n
BazDan W-(P) #bda G18-p
1015:15-16:00PKM K04-(N. #Pkm A227-nPPSystM K04-(n. #Psm J209-n
PPSystM K04-(p. #PSm J209-p
 WspInfPM P04-(N) #Wpm A338-n
PSieciKP W-(P) #PKP A437-p
BazDan W-(N) #BDa G18-n
BazDan W-(P) #bda G18-p
1116:15-17:00PrSteroP L04-(N. #Psp G107-n
PrSteroP L04-(P. #psP G107-p
PPSystM K01-(n. GF J207.1-n
PPSystM K01-(p. FG J207.1-p
 PPSystM W-(N) #PSM G18-n
PAplikInt W-(P) #PAI G18-p
BazDan W-(N) #BDa G18-n
BazDan W-(P) #bda G18-p
1217:00-17:45PrSteroP L04-(N. #Psp G107-n
PrSteroP L04-(P. #psP G107-p
PPSystM K01-(n. GF J207.1-n
PPSystM K01-(p. FG J207.1-p
 PPSystM W-(N) #PSM G18-n
PAplikInt W-(P) #PAI G18-p
 
1318:00-18:45SocPsychP Ć-(N) JJ A409-nBazDan K01-(N) PB G117-n
BazDan K01-(P) BP G117-p
termin dodatkowy Katedra M7termin dodatkowy Katedra M7 
1418:45-19:30SocPsychP Ć-(N) JJ A409-nBazDan K01-(N) PB G117-n
BazDan K01-(P) BP G117-p
termin dodatkowy Katedra M7termin dodatkowy Katedra M7 
1519:45-20:30PrSteroP W-(N) #psp G18-nBazDan K01-(N) PB G117-n
BazDan K01-(P) BP G117-p
   
1620:30-21:15PrSteroP W-(N) #psp G18-n    
Drukuj plan + + + + + + + +
wygenerowano 02.06.2025
za pomocą programu Plan lekcji Optivum
firmy VULCAN
logo programu Plan lekcji Optivum
+
+ + + """; + + String listHTML = """ + + + + + + Lista oddziałów, nauczycieli i sal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Oddziały
+
+

11A1

+

11K2

+

12K1

+

12K2

+

12K3

+
Nauczyciele
+
+
Sale
+
+
+ + + """; + + +} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java index bd1a630..7c5c98e 100644 --- a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java +++ b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java @@ -1,16 +1,28 @@ package org.pkwmtt.cache; import org.junit.jupiter.api.Test; +import org.pkwmtt.ValuesForTest; import org.pkwmtt.timetable.TimetableCacheService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; +import org.springframework.test.context.ActiveProfiles; +import org.wiremock.spring.ConfigureWireMock; +import org.wiremock.spring.EnableWireMock; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; @SpringBootTest +@ActiveProfiles("test") +@EnableWireMock({ + @ConfigureWireMock( + name = "my-mock", + port = 8888) +}) class CacheConfigTest { @Autowired private TimetableCacheService service; @@ -20,22 +32,33 @@ class CacheConfigTest { @Test void testCacheKeyPresent_Schedule() { - service.getGeneralGroupSchedule("12K1"); - - Cache cache = cacheManager.getCache("timetables"); - assertThat(cache).isNotNull(); + //given + initWireMock(); - Cache.ValueWrapper wrapper = cache.get("timetable_12K1"); - assertThat(wrapper).isNotNull(); - assertThat(wrapper.get()).isInstanceOf(String.class); + //when + service.getGeneralGroupSchedule("12K1"); + var cache = cacheManager.getCache("timetables"); + //then + assertAll( + () -> { + assertThat(cache).isNotNull(); + assertThat(cache.get("generalGroupMap", String.class)) + .isEqualTo("{\"11K2\":\"plany/o8.html\",\"12K1\":\"plany/o25.html\",\"11A1\":\"plany/o1.html\",\"12K3\":\"plany/o27.html\",\"12K2\":\"plany/o26.html\"}"); + }, + () -> { + var wrapper = cache.get("timetable_12K1"); + assertThat(wrapper).isNotNull(); + assertThat(wrapper.get()).isInstanceOf(String.class); + } + ); } @Test void testCacheKeyPresent_HoursList(){ service.getListOfHours(); - Cache cache = cacheManager.getCache("timetables"); + var cache = cacheManager.getCache("timetables"); assertThat(cache).isNotNull(); Cache.ValueWrapper wrapper = cache.get("hourList"); @@ -43,4 +66,18 @@ void testCacheKeyPresent_HoursList(){ assertThat(wrapper.get()).isInstanceOf(String.class); } + private void initWireMock() { + stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } + } \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java b/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java index 063db82..5815e19 100644 --- a/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java @@ -4,17 +4,32 @@ import org.jsoup.nodes.Document; import org.junit.jupiter.api.Test; import org.junit.runners.Suite; +import org.pkwmtt.ValuesForTest; import org.pkwmtt.timetable.dto.TimetableDTO; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ActiveProfiles; +import org.wiremock.spring.ConfigureWireMock; +import org.wiremock.spring.EnableWireMock; import java.io.IOException; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest @Suite.SuiteClasses(TimetableParserService.class) +@ActiveProfiles("test") +@EnableWireMock({ + @ConfigureWireMock( + name = "my-mock", + port = 8888) +}) class ParserServiceTest { TimetableParserService parserService; @@ -22,6 +37,8 @@ class ParserServiceTest { parserService = new TimetableParserService(); } + @Value("${main.url:https://podzial.mech.pk.edu.pl/stacjonarne/html/}") + private String mainUrl; @Test public void checkParserDataFor12K1_Monday_First() throws IOException { @@ -63,17 +80,37 @@ public void isHoursListCorrect() throws IOException { @Test @WithMockUser public void isGeneralGroupListCorrect() throws IOException { + //given + initWireMock(); + + //when //fetch data Document document = Jsoup - .connect("http://podzial.mech.pk.edu.pl/stacjonarne/html/lista.html") + .connect(mainUrl + "lista.html") .get(); //call method var result = parserService.parseGeneralGroups(document.html()); + + //then //Check if list contains specific elements assertTrue(result.containsKey("12K1")); assertTrue(result.containsKey("11A1")); assertTrue(result.containsKey("11K2")); assertEquals("plany/o25.html", result.get("12K1")); } + + private void initWireMock() { + stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } } diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties new file mode 100644 index 0000000..a8c4430 --- /dev/null +++ b/src/test/resources/application-test.properties @@ -0,0 +1 @@ +main.url = http://localhost:8888/ \ No newline at end of file From 9ebf1cc870f79e3fd11e1281ec6df643c90bc50e Mon Sep 17 00:00:00 2001 From: Krecik Date: Wed, 13 Aug 2025 21:46:16 +0200 Subject: [PATCH 22/37] Add more wiremock into tests --- .../timetable/TimetableCacheService.java | 10 +- .../pkwmtt/timetable/TimetableController.java | 7 +- .../pkwmtt/timetable/TimetableService.java | 18 ++-- src/main/resources/application.properties | 3 + .../org/pkwmtt/cache/CacheConfigTest.java | 73 +++++++-------- .../java/org/pkwmtt/cache/CacheInspector.java | 4 +- .../timetable/TimetableCacheServiceTest.java | 78 ++++++++++++---- .../timetable/TimetableControllerTest.java | 45 +++++++-- .../timetable/TimetableServiceTest.java | 92 +++++++++++-------- .../timetable/parser/ParserServiceTest.java | 71 +++++++------- src/test/java/test/TestConfig.java | 19 ++++ .../resources/application-test.properties | 2 +- 12 files changed, 256 insertions(+), 166 deletions(-) create mode 100644 src/test/java/test/TestConfig.java diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 7babc6d..09ad1d1 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static java.util.Objects.isNull; @@ -94,11 +93,6 @@ public Map getGeneralGroupsMap () throws WebPageContentNotAvaila } ); } - public List getGeneralGroupsList() { - return getGeneralGroupsMap().keySet().stream() - .sorted() - .collect(Collectors.toList()); - } /** * Retrieves the standard list of hour ranges used in the timetable. @@ -106,8 +100,8 @@ public List getGeneralGroupsList() { * @return list of hour labels (e.g., 08:00–09:30) * @throws WebPageContentNotAvailableException if hour definition page can't be loaded */ - public List getListOfHours () throws WebPageContentNotAvailableException { - String url = "https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html"; + public List getListOfHours () throws WebPageContentNotAvailableException { + String url = mainUrl + "plany/o25.html"; String json = cache.get( "hourList", () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) diff --git a/src/main/java/org/pkwmtt/timetable/TimetableController.java b/src/main/java/org/pkwmtt/timetable/TimetableController.java index a37f66a..7110810 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableController.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableController.java @@ -29,7 +29,9 @@ public class TimetableController { * @throws WebPageContentNotAvailableException . */ @GetMapping("/{generalGroupName}") - public ResponseEntity getGeneralGroupSchedule (@PathVariable String generalGroupName, @RequestParam(required = false, name = "sub") List subgroups) + public ResponseEntity getGeneralGroupSchedule ( + @PathVariable String generalGroupName, + @RequestParam(required = false, name = "sub") List subgroups) throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException, SpecifiedSubGroupDoesntExistsException, JsonProcessingException { @@ -37,8 +39,7 @@ public ResponseEntity getGeneralGroupSchedule (@PathVariable Strin return ResponseEntity.ok(cachedService.getGeneralGroupSchedule(generalGroupName)); } return ResponseEntity.ok(service.getFilteredGeneralGroupSchedule( - generalGroupName.toUpperCase(), - subgroups + generalGroupName, subgroups )); } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index 33ceadd..c0e6ca3 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -14,6 +14,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j @Service @@ -56,10 +57,9 @@ public List getAvailableSubGroups (String generalGroupName) matchedGroups.add(text); } - List result = new ArrayList<>(matchedGroups.stream().toList()); - Collections.sort(result); - - return result; + return matchedGroups.stream() + .sorted() + .toList(); } @@ -103,13 +103,9 @@ public TimetableDTO getFilteredGeneralGroupSchedule (String generalGroupName, Li * @return List of general group's names */ public List getGeneralGroupList () throws WebPageContentNotAvailableException { - var result = new ArrayList<>(cachedService - .getGeneralGroupsMap() - .keySet() - .stream() - .toList()); - Collections.sort(result); - return result; + return cachedService.getGeneralGroupsMap().keySet().stream() + .sorted() + .collect(Collectors.toList()); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index cd609c7..4c1aef9 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -15,3 +15,6 @@ logging.file.name=logs/app.log logging.file.path=logs spring.cache.type=caffeine + +logging.level.WireMock.my-mock=off + diff --git a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java index 7c5c98e..6aa8251 100644 --- a/src/test/java/org/pkwmtt/cache/CacheConfigTest.java +++ b/src/test/java/org/pkwmtt/cache/CacheConfigTest.java @@ -1,39 +1,44 @@ package org.pkwmtt.cache; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.pkwmtt.ValuesForTest; import org.pkwmtt.timetable.TimetableCacheService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import org.springframework.test.context.ActiveProfiles; -import org.wiremock.spring.ConfigureWireMock; -import org.wiremock.spring.EnableWireMock; +import test.TestConfig; -import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; - -@SpringBootTest -@ActiveProfiles("test") -@EnableWireMock({ - @ConfigureWireMock( - name = "my-mock", - port = 8888) -}) -class CacheConfigTest { +class CacheConfigTest extends TestConfig { @Autowired private TimetableCacheService service; @Autowired private CacheManager cacheManager; + @BeforeEach + public void initWireMock() { + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } + @Test void testCacheKeyPresent_Schedule() { //given - initWireMock(); //when service.getGeneralGroupSchedule("12K1"); @@ -56,28 +61,24 @@ void testCacheKeyPresent_Schedule() { @Test void testCacheKeyPresent_HoursList(){ - service.getListOfHours(); + //given + //when + service.getListOfHours(); var cache = cacheManager.getCache("timetables"); - assertThat(cache).isNotNull(); - - Cache.ValueWrapper wrapper = cache.get("hourList"); - assertThat(wrapper).isNotNull(); - assertThat(wrapper.get()).isInstanceOf(String.class); - } - - private void initWireMock() { - stubFor(get(urlPathMatching("/plany/o25.html")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/*") - .withBody(ValuesForTest.timetableHTML))); - stubFor(get(urlPathMatching("/lista.html")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/*") - .withBody(ValuesForTest.listHTML))); + //then + assertAll( + () -> { + assertThat(cache).isNotNull(); + assertThat(cache.get("hourList", String.class)) + .isEqualTo("[\"7:30- 8:15\",\"8:15- 9:00\",\"9:15-10:00\",\"10:00-10:45\",\"11:00-11:45\",\"11:45-12:30\",\"12:45-13:30\",\"13:30-14:15\",\"14:30-15:15\",\"15:15-16:00\",\"16:15-17:00\",\"17:00-17:45\",\"18:00-18:45\",\"18:45-19:30\",\"19:45-20:30\",\"20:30-21:15\"]"); + }, + () -> { + var wrapper = cache.get("hourList"); + assertThat(wrapper).isNotNull(); + assertThat(wrapper.get()).isInstanceOf(String.class); + } + ); } - } \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/cache/CacheInspector.java b/src/test/java/org/pkwmtt/cache/CacheInspector.java index 6aa6403..0b38463 100644 --- a/src/test/java/org/pkwmtt/cache/CacheInspector.java +++ b/src/test/java/org/pkwmtt/cache/CacheInspector.java @@ -9,6 +9,8 @@ import java.util.Map; +import static java.util.Objects.isNull; + @Component @RequiredArgsConstructor @SuppressWarnings("unused") @@ -20,7 +22,7 @@ public class CacheInspector { public Map getAllEntries (String cacheName) { CaffeineCache springCache = (CaffeineCache) cacheManager.getCache(cacheName); - if (springCache == null) { + if (isNull(springCache)) { throw new IllegalArgumentException("No cache with name " + cacheName); } diff --git a/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java b/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java index b84ec4d..f25888f 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableCacheServiceTest.java @@ -1,20 +1,26 @@ package org.pkwmtt.timetable; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.pkwmtt.ValuesForTest; import org.pkwmtt.cache.CacheInspector; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; +import test.TestConfig; import java.util.List; import java.util.Map; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; @Slf4j -@SpringBootTest -class TimetableCacheServiceTest { +class TimetableCacheServiceTest extends TestConfig { @Autowired TimetableCacheService cachedService; @@ -23,56 +29,89 @@ class TimetableCacheServiceTest { @Autowired CacheInspector cacheInspector; - + + @BeforeEach + public void initWireMock() { + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } @Test public void shouldHourListBePresentInCache () { //given - String key = "hourList"; + var key = "hourList"; cachedService.getListOfHours(); // call method to save data in cache //when Map cacheData = cacheInspector.getAllEntries("timetables"); // get all keys saved in cache //then - assertNotNull(cacheData); - assertTrue(cacheData.containsKey(key)); - assertNotNull(cacheData.get(key)); + assertAll( + () -> assertNotNull(cacheData), + () -> assertTrue(cacheData.containsKey(key)), + () -> { + var hourList = cacheData.get(key); + assertNotNull(hourList); + assertThat(hourList).isEqualTo("[\"7:30- 8:15\",\"8:15- 9:00\",\"9:15-10:00\",\"10:00-10:45\",\"11:00-11:45\",\"11:45-12:30\",\"12:45-13:30\",\"13:30-14:15\",\"14:30-15:15\",\"15:15-16:00\",\"16:15-17:00\",\"17:00-17:45\",\"18:00-18:45\",\"18:45-19:30\",\"19:45-20:30\",\"20:30-21:15\"]"); + } + ); } @Test public void shouldReturnGeneralGroupsMap () { //given - Map result = cachedService.getGeneralGroupsMap(); - + var expectedMap = Map.of( + "11K2", "plany/o8.html", + "12K1", "plany/o25.html", + "11A1", "plany/o1.html", + "12K3", "plany/o27.html", + "12K2", "plany/o26.html" + ); + //when - + var result = cachedService.getGeneralGroupsMap(); + //then - assertNotNull(result); - assertFalse(result.isEmpty()); + assertThat(result).isEqualTo(expectedMap); } @Test public void shouldGeneralGroupMapBePresentInCache () { //given - String key = "generalGroupMap"; + var key = "generalGroupMap"; + var expectedValue = "{\"11K2\":\"plany/o8.html\",\"12K1\":\"plany/o25.html\",\"11A1\":\"plany/o1.html\",\"12K3\":\"plany/o27.html\",\"12K2\":\"plany/o26.html\"}"; cachedService.getGeneralGroupsMap(); // call method to save data in cache //when Map cacheData = cacheInspector.getAllEntries("timetables"); // get all keys saved in cache //then - assertNotNull(cacheData); - assertTrue(cacheData.containsKey(key)); - assertNotNull(cacheData.get(key)); + assertAll( + () -> assertNotNull(cacheData), + () -> { + assertTrue(cacheData.containsKey(key)); + var data = cacheData.get(key); + assertThat(data).isEqualTo(expectedValue); + } + ); } @Test + @Disabled("Test shouldn't be random") public void shouldReturnRandomGeneralGroupSchedule () { //given List generalGroupList = service.getGeneralGroupList(); - String generalGroupName = generalGroupList.get((int) (Math.random() * generalGroupList.size())); // get random general group + var generalGroupName = generalGroupList.get((int) (Math.random() * generalGroupList.size())); // get random general group //when var result = cachedService.getGeneralGroupSchedule(generalGroupName); @@ -83,6 +122,7 @@ public void shouldReturnRandomGeneralGroupSchedule () { } @Test + @Disabled("Test shouldn't be random") public void shouldRandomGeneralGroupScheduleBePresentInCache () { //given List generalGroupList = service.getGeneralGroupList(); @@ -100,6 +140,4 @@ public void shouldRandomGeneralGroupScheduleBePresentInCache () { assertTrue(cacheData.containsKey(key)); assertInstanceOf(String.class, cacheData.get(key)); } - - } \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java index 30e8396..9c5c290 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableControllerTest.java @@ -1,17 +1,19 @@ package org.pkwmtt.timetable; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.pkwmtt.ValuesForTest; import org.pkwmtt.exceptions.dto.ErrorResponseDTO; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import test.TestConfig; import java.time.LocalDateTime; import java.util.Arrays; @@ -19,24 +21,39 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.*; @Slf4j -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -class TimetableControllerTest { +class TimetableControllerTest extends TestConfig { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; + + @BeforeEach + public void initWireMock() { + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } @Test public void testGetGeneralGroupScheduleFiltered_withOptionalParams () { //given - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1?sub=K01&sub=L01&sub=P01", + var url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1?sub=K01&sub=L01&sub=P01", port ); @@ -44,17 +61,25 @@ public void testGetGeneralGroupScheduleFiltered_withOptionalParams () { ResponseEntity response = restTemplate.getForEntity(url, TimetableDTO.class); //then - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getBody()); - assertEquals(5, response.getBody().getData().size()); - assertEquals(12, response.getBody().getData().getFirst().getOdd().size()); - assertEquals(6, response.getBody().getData().getFirst().getEven().size()); + assertAll( + () -> assertEquals(HttpStatus.OK, response.getStatusCode()), + () -> { + var responseBody = response.getBody(); + assertNotNull(responseBody); + }, + () -> { + var responseData = response.getBody().getData(); + assertEquals(5, responseData.size()); + assertEquals(12, responseData.getFirst().getOdd().size()); + assertEquals(6, responseData.getFirst().getEven().size()); + } + ); } @Test public void testGetGeneralGroupScheduleFiltered_withoutParams () { //given - String url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1", port); + var url = String.format("http://localhost:%s/pkmwtt/api/v1/timetables/12K1", port); //when ResponseEntity response = restTemplate.getForEntity(url, TimetableDTO.class); diff --git a/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java b/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java index 9dd84aa..c582a3f 100644 --- a/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/TimetableServiceTest.java @@ -1,63 +1,79 @@ package org.pkwmtt.timetable; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.pkwmtt.ValuesForTest; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; +import test.TestConfig; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.regex.Matcher; import java.util.regex.Pattern; - -@SpringBootTest -class TimetableServiceTest { +class TimetableServiceTest extends TestConfig { @Autowired private TimetableService service; + + @BeforeEach + public void initWireMock() { + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } @Test public void shouldReturnAvailableSubGroups () throws JsonProcessingException { //given - String generalGroupName = "12K1"; - String regex = "^[A-Z]\\d{2}$"; - Pattern pattern = Pattern.compile(regex); + var generalGroupName = "12K1"; + var regex = "^[A-Z]\\d{2}$"; + var pattern = Pattern.compile(regex); + var expectedResult = List.of("K01", "K04", "L01", "L02", "L04", "P01", "P04"); //when var result = service.getAvailableSubGroups(generalGroupName); //then - assertNotNull(result); - assertFalse(result.isEmpty()); - - result.forEach(item -> { - Matcher matcher = pattern.matcher(item); - if (!matcher.find()) { - fail("Wrong subgroup format"); - } - }); - + assertThat(result).isEqualTo(expectedResult); + + //I don't know why it is in test. I think it is for debug? +// result.forEach(item -> { +// Matcher matcher = pattern.matcher(item); +// if (!matcher.find()) { +// fail("Wrong subgroup format"); +// } +// }); } @Test public void shouldThrow_SpecifiedGeneralGroupDoesntExistsException () { //given - List subgroups = List.of("K01", "L01"); - String generalGroupName = "77Z3"; + var subgroups = List.of("K01", "L01"); + var generalGroupName = "77Z3"; //when //then assertThrows( - SpecifiedGeneralGroupDoesntExistsException.class, () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) + SpecifiedGeneralGroupDoesntExistsException.class, + () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) ); - } @Test @@ -69,25 +85,29 @@ public void shouldThrow_SpecifiedSubGroupDoesntExistsException () { //then assertThrows( - SpecifiedSubGroupDoesntExistsException.class, () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) + SpecifiedSubGroupDoesntExistsException.class, + () -> service.getFilteredGeneralGroupSchedule(generalGroupName, subgroups) ); - } - @Test public void shouldReturnSortedGeneralGroupList () { //given - List result = service.getGeneralGroupList(); - List originalResult = new ArrayList<>(result); + var expectedResult = List.of( + "11A1", + "11K2", + "12K1", + "12K2", + "12K3" + ); //when - Collections.sort(result); - + var result = service.getGeneralGroupList(); + //then - assertEquals(originalResult, result); - assertNotNull(result); - assertFalse(result.isEmpty()); - + assertAll( + () -> assertNotNull(result), + () -> assertFalse(result.isEmpty()), + () -> assertEquals(expectedResult, result) + ); } - } \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java b/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java index 5815e19..9dd2567 100644 --- a/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java +++ b/src/test/java/org/pkwmtt/timetable/parser/ParserServiceTest.java @@ -2,58 +2,64 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runners.Suite; import org.pkwmtt.ValuesForTest; import org.pkwmtt.timetable.dto.TimetableDTO; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ActiveProfiles; -import org.wiremock.spring.ConfigureWireMock; -import org.wiremock.spring.EnableWireMock; +import test.TestConfig; import java.io.IOException; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -@SpringBootTest @Suite.SuiteClasses(TimetableParserService.class) -@ActiveProfiles("test") -@EnableWireMock({ - @ConfigureWireMock( - name = "my-mock", - port = 8888) -}) -class ParserServiceTest { +class ParserServiceTest extends TestConfig { TimetableParserService parserService; { parserService = new TimetableParserService(); } - @Value("${main.url:https://podzial.mech.pk.edu.pl/stacjonarne/html/}") + @Value("${main.url}") private String mainUrl; + @BeforeEach + public void initWireMock() { + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/plany/o25.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.timetableHTML))); + + EXTERNAL_SERVICE_API_MOCK.stubFor(get(urlPathMatching("/lista.html")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/*") + .withBody(ValuesForTest.listHTML))); + } + @Test public void checkParserDataFor12K1_Monday_First() throws IOException { + //given //fetch 12K1 Document document = Jsoup - .connect("https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html") + .connect(mainUrl + "plany/o25.html") .get(); //Create object - TimetableDTO timeTable = new TimetableDTO("12K1"); + var timeTable = new TimetableDTO("12K1"); - //Call method + //when timeTable.setData(parserService.parse(document.html())); - //Tests + //then assertEquals("12K1", timeTable.getName()); assertEquals("Poniedziałek", timeTable.getData().getFirst().getName()); assertEquals(5, timeTable.getData().size()); @@ -61,16 +67,17 @@ public void checkParserDataFor12K1_Monday_First() throws IOException { @Test public void isHoursListCorrect() throws IOException { - + //given //fetch data Document document = Jsoup - .connect("https://podzial.mech.pk.edu.pl/stacjonarne/html/plany/o25.html") + .connect(mainUrl + "plany/o25.html") .get(); - + //when //call function var result = parserService.parseHours(document.html()); + //then //Check first, last and middle element assertEquals("7:30- 8:15", result.getFirst()); assertEquals("12:45-13:30", result.get(6)); @@ -81,14 +88,12 @@ public void isHoursListCorrect() throws IOException { @WithMockUser public void isGeneralGroupListCorrect() throws IOException { //given - initWireMock(); - - //when //fetch data Document document = Jsoup - .connect(mainUrl + "lista.html") - .get(); + .connect(mainUrl + "lista.html") + .get(); + //when //call method var result = parserService.parseGeneralGroups(document.html()); @@ -99,18 +104,4 @@ public void isGeneralGroupListCorrect() throws IOException { assertTrue(result.containsKey("11K2")); assertEquals("plany/o25.html", result.get("12K1")); } - - private void initWireMock() { - stubFor(get(urlPathMatching("/plany/o25.html")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/*") - .withBody(ValuesForTest.timetableHTML))); - - stubFor(get(urlPathMatching("/lista.html")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/*") - .withBody(ValuesForTest.listHTML))); - } } diff --git a/src/test/java/test/TestConfig.java b/src/test/java/test/TestConfig.java new file mode 100644 index 0000000..aed8624 --- /dev/null +++ b/src/test/java/test/TestConfig.java @@ -0,0 +1,19 @@ +package test; + +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +public abstract class TestConfig { + + protected static final int WIREMOCK_PORT = 9999; + + @RegisterExtension + protected static final WireMockExtension EXTERNAL_SERVICE_API_MOCK = WireMockExtension.newInstance() + .options(wireMockConfig().port(WIREMOCK_PORT)).build(); +} diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index a8c4430..6224c97 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -1 +1 @@ -main.url = http://localhost:8888/ \ No newline at end of file +main.url = http://localhost:9999/ \ No newline at end of file From a9a7f06306ba735946f2efb83cb98b4d9cff22d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Piotrkowski?= Date: Wed, 13 Aug 2025 21:55:52 +0200 Subject: [PATCH 23/37] fix: update database schema and JPA entities --- src/main/java/org/pkwmtt/entity/Exam.java | 26 ++++++++++++------- src/main/java/org/pkwmtt/entity/ExamType.java | 8 +++--- .../java/org/pkwmtt/entity/GeneralGroup.java | 8 ++---- src/main/java/org/pkwmtt/entity/Group.java | 24 ----------------- src/main/java/org/pkwmtt/entity/OTPCode.java | 13 +++++----- .../java/org/pkwmtt/entity/StudentGroup.java | 23 ++++++++++++++++ src/main/java/org/pkwmtt/entity/User.java | 14 +++++----- .../pkwmtt/repository/GroupRepository.java | 4 +-- 8 files changed, 62 insertions(+), 58 deletions(-) delete mode 100644 src/main/java/org/pkwmtt/entity/Group.java create mode 100644 src/main/java/org/pkwmtt/entity/StudentGroup.java diff --git a/src/main/java/org/pkwmtt/entity/Exam.java b/src/main/java/org/pkwmtt/entity/Exam.java index 56f6fd8..6cda359 100644 --- a/src/main/java/org/pkwmtt/entity/Exam.java +++ b/src/main/java/org/pkwmtt/entity/Exam.java @@ -2,29 +2,37 @@ import jakarta.persistence.*; import lombok.Data; - -import java.util.Date; +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; @Entity @Table(name = "`exams`") @Data public class Exam { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "exam_id") private Integer examId; + @Column(nullable = false) private String title; private String description; - private Date date; - //private Instant date; Proposition to change - - @Column(name = "`groups`") - private String examGroup; + @Column(name = "`exam_date`", nullable = false) + private LocalDateTime examDate; @ManyToOne - @JoinColumn(name = "exam_type_id") + @JoinColumn(name = "exam_type_id", nullable = false) private ExamType examType; + + @ManyToMany + @JoinTable( + name="exams_groups", + joinColumns = @JoinColumn(name = "exam_id"), + inverseJoinColumns = @JoinColumn(name = "group_id") + ) + private Set groups = new HashSet<>();; + } diff --git a/src/main/java/org/pkwmtt/entity/ExamType.java b/src/main/java/org/pkwmtt/entity/ExamType.java index 5df84a4..fd971ef 100644 --- a/src/main/java/org/pkwmtt/entity/ExamType.java +++ b/src/main/java/org/pkwmtt/entity/ExamType.java @@ -7,10 +7,12 @@ @Data @Table(name = "`exam_type`") public class ExamType { + @Id - @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "exam_type_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "exam_type_id", nullable = false) private Integer examTypeId; + @Column(nullable = false) private String name; -} \ No newline at end of file +} diff --git a/src/main/java/org/pkwmtt/entity/GeneralGroup.java b/src/main/java/org/pkwmtt/entity/GeneralGroup.java index 2aa1d23..eb00158 100644 --- a/src/main/java/org/pkwmtt/entity/GeneralGroup.java +++ b/src/main/java/org/pkwmtt/entity/GeneralGroup.java @@ -3,19 +3,15 @@ import jakarta.persistence.*; import lombok.Data; -import java.util.Set; - @Entity @Data @Table(name = "`general_group`") public class GeneralGroup { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "general_group_id") private Integer generalGroupId; + @Column(nullable = false) private String name; - - @OneToMany(mappedBy = "generalGroup") - private Set groups; } diff --git a/src/main/java/org/pkwmtt/entity/Group.java b/src/main/java/org/pkwmtt/entity/Group.java deleted file mode 100644 index 43ed486..0000000 --- a/src/main/java/org/pkwmtt/entity/Group.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; - -@Entity -@Data -@Table(name = "`groups`") -//Recommended change name of this class. Group is key word in sql and may lead to misunderstandings -public class Group { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "group_id") - private Integer groupId; - - private String name; - - @Column(name = "group_count") - private int groupCount; - - @ManyToOne - @JoinColumn(name = "general_group_id") - private GeneralGroup generalGroup; -} diff --git a/src/main/java/org/pkwmtt/entity/OTPCode.java b/src/main/java/org/pkwmtt/entity/OTPCode.java index 6e8774d..a202b42 100644 --- a/src/main/java/org/pkwmtt/entity/OTPCode.java +++ b/src/main/java/org/pkwmtt/entity/OTPCode.java @@ -2,7 +2,6 @@ import jakarta.persistence.*; import lombok.Data; - import java.time.LocalDateTime; @Entity @@ -10,17 +9,17 @@ @Table(name = "otp_codes") public class OTPCode { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "otp_code_id") private Integer otpCodeId; + @Column(nullable = false) private String code; - private LocalDateTime timestamp; - - private boolean used; + @Column(nullable = false) + private LocalDateTime expire; @OneToOne - @JoinColumn(name = "user_id", unique = true) - private User user; + @JoinColumn(name = "`general_group_id`", nullable = false) + private GeneralGroup generalGroup; } diff --git a/src/main/java/org/pkwmtt/entity/StudentGroup.java b/src/main/java/org/pkwmtt/entity/StudentGroup.java new file mode 100644 index 0000000..5c0f10c --- /dev/null +++ b/src/main/java/org/pkwmtt/entity/StudentGroup.java @@ -0,0 +1,23 @@ +package org.pkwmtt.entity; + +import jakarta.persistence.*; +import lombok.Data; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Data +@Table(name = "`groups`") +public class StudentGroup { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "group_id") + private Integer groupId; + + @Column(nullable = false) + private String name; + + @ManyToMany(mappedBy = "groups") + private Set exams = new HashSet<>(); +} diff --git a/src/main/java/org/pkwmtt/entity/User.java b/src/main/java/org/pkwmtt/entity/User.java index 9ea5060..81a03c8 100644 --- a/src/main/java/org/pkwmtt/entity/User.java +++ b/src/main/java/org/pkwmtt/entity/User.java @@ -9,21 +9,21 @@ @Table(name = "`users`") public class User { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Integer userId; - @ManyToOne - @JoinColumn(name = "general_group_id") + @OneToOne + @JoinColumn(name = "general_group_id", nullable = false) private GeneralGroup generalGroup; + @Column(nullable = false) private String email; - @Column(name = "is_active") + @Column(name = "is_active", nullable = false) private boolean isActive; + @Enumerated(EnumType.STRING) + @Column(nullable = false) private Role role; - - @OneToOne(mappedBy = "user") - private OTPCode otpCode; } diff --git a/src/main/java/org/pkwmtt/repository/GroupRepository.java b/src/main/java/org/pkwmtt/repository/GroupRepository.java index ae46110..b2396a9 100644 --- a/src/main/java/org/pkwmtt/repository/GroupRepository.java +++ b/src/main/java/org/pkwmtt/repository/GroupRepository.java @@ -1,7 +1,7 @@ package org.pkwmtt.repository; -import org.pkwmtt.entity.Group; +import org.pkwmtt.entity.StudentGroup; import org.springframework.data.jpa.repository.JpaRepository; -public interface GroupRepository extends JpaRepository { +public interface GroupRepository extends JpaRepository { } \ No newline at end of file From cc652817c79e1ae7b70df3a43cb349b714c69e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Piotrkowski?= Date: Wed, 13 Aug 2025 22:19:30 +0200 Subject: [PATCH 24/37] fix: update new init.sql --- init.sql | 263 ++----------------------------------------------------- 1 file changed, 9 insertions(+), 254 deletions(-) diff --git a/init.sql b/init.sql index df5e8a1..d31ad78 100644 --- a/init.sql +++ b/init.sql @@ -3,7 +3,7 @@ -- https://www.phpmyadmin.net/ -- -- Host: db --- Generation Time: Lip 31, 2025 at 06:56 PM +-- Generation Time: Aug 13, 2025 at 08:18 PM -- Wersja serwera: 9.3.0 -- Wersja PHP: 8.2.27 @@ -20,278 +20,33 @@ SET time_zone = "+00:00"; -- -- Baza danych: `pktt` -- -CREATE DATABASE IF NOT EXISTS `pktt` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; -USE `pktt`; -- -------------------------------------------------------- --- --- Struktura tabeli dla tabeli `exams` --- - -DROP TABLE IF EXISTS `exams`; -CREATE TABLE `exams` ( - `exam_id` int NOT NULL, - `title` varchar(255) DEFAULT NULL, - `description` varchar(255) DEFAULT NULL, - `date` datetime(6) DEFAULT NULL, - `groups` varchar(255) DEFAULT NULL, - `exam_type_id` int NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- --- Tabela Truncate przed wstawieniem `exams` --- - -TRUNCATE TABLE `exams`; --- --- Zrzut danych tabeli `exams` --- - -INSERT INTO `exams` (`exam_id`, `title`, `description`, `date`, `groups`, `exam_type_id`) VALUES -(1, 'Matematyka Dyskretna', 'Egzamin końcowy z matematyki dyskretnej', '2025-07-30 00:00:00.000000', '12K3,11L1', 2), -(2, 'Programowanie C++', 'Kolokwium z programowania w C++', '2025-08-05 00:00:00.000000', '12K2,13S3', 1), -(3, 'Sieci Komputerowe', 'Projekt zespołowy na sieciach komputerowych', '2025-09-10 00:00:00.000000', '14S4,12K1', 3); - --- -------------------------------------------------------- - --- --- Struktura tabeli dla tabeli `exam_type` --- - -DROP TABLE IF EXISTS `exam_type`; -CREATE TABLE `exam_type` ( - `exam_type_id` int NOT NULL, - `name` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- --- Tabela Truncate przed wstawieniem `exam_type` --- - -TRUNCATE TABLE `exam_type`; --- --- Zrzut danych tabeli `exam_type` --- - -INSERT INTO `exam_type` (`exam_type_id`, `name`) VALUES -(1, 'Kolokwium'), -(2, 'Egzamin końcowy'), -(3, 'Projekt'); - --- -------------------------------------------------------- - --- --- Struktura tabeli dla tabeli `general_group` --- - -DROP TABLE IF EXISTS `general_group`; -CREATE TABLE `general_group` ( - `general_group_id` int NOT NULL, - `name` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- --- Tabela Truncate przed wstawieniem `general_group` --- - -TRUNCATE TABLE `general_group`; --- --- Zrzut danych tabeli `general_group` --- - -INSERT INTO `general_group` (`general_group_id`, `name`) VALUES -(11, '1'), -(12, '2'), -(13, '3'), -(14, '4'); - --- -------------------------------------------------------- - --- --- Struktura tabeli dla tabeli `groups` --- - -DROP TABLE IF EXISTS `groups`; -CREATE TABLE `groups` ( - `group_id` int NOT NULL, - `letter` char(1) NOT NULL, - `group_count` int NOT NULL, - `general_group_id` int NOT NULL, - `name` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- --- Tabela Truncate przed wstawieniem `groups` --- - -TRUNCATE TABLE `groups`; --- --- Zrzut danych tabeli `groups` --- - -INSERT INTO `groups` (`group_id`, `letter`, `group_count`, `general_group_id`, `name`) VALUES -(1, 'K', 1, 11, NULL), -(2, 'K', 2, 12, NULL), -(3, 'L', 1, 11, NULL), -(4, 'L', 2, 12, NULL), -(5, 'S', 3, 13, NULL), -(6, 'S', 4, 14, NULL), -(7, 'K', 3, 12, NULL), -(8, 'L', 4, 14, NULL); - --- -------------------------------------------------------- - --- --- Struktura tabeli dla tabeli `otp_codes` --- - -DROP TABLE IF EXISTS `otp_codes`; -CREATE TABLE `otp_codes` ( - `code` varchar(255) DEFAULT NULL, - `expire` timestamp NOT NULL, - `used` tinyint(1) NOT NULL, - `user_id` int NOT NULL, - `otp_code_id` int NOT NULL, - `timestamp` datetime(6) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- --- Tabela Truncate przed wstawieniem `otp_codes` --- - -TRUNCATE TABLE `otp_codes`; --- -------------------------------------------------------- - -- -- Struktura tabeli dla tabeli `users` -- DROP TABLE IF EXISTS `users`; -CREATE TABLE `users` ( - `user_id` int NOT NULL, +CREATE TABLE IF NOT EXISTS `users` ( + `user_id` int NOT NULL AUTO_INCREMENT, `general_group_id` int NOT NULL, - `email` varchar(254) NOT NULL, - `is_active` tinyint(1) NOT NULL, - `role` enum('ADMIN','REPRESENTATIVE') NOT NULL + `email` varchar(255) NOT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT '1', + `role` enum('ADMIN','REPRESENTATIVE') NOT NULL DEFAULT 'REPRESENTATIVE', + PRIMARY KEY (`user_id`), + KEY `general_group_id_idx` (`general_group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; --- --- Tabela Truncate przed wstawieniem `users` --- - -TRUNCATE TABLE `users`; --- --- Zrzut danych tabeli `users` --- - -INSERT INTO `users` (`user_id`, `general_group_id`, `email`, `is_active`, `role`) VALUES -(1, 12, 'jan.kowalski@example.com', 1, 'ADMIN'), -(2, 11, 'anna.nowak@example.com', 1, 'REPRESENTATIVE'), -(3, 13, 'piotr.zielinski@example.com', 0, 'REPRESENTATIVE'), -(4, 14, 'ewa.wisniewska@example.com', 1, 'ADMIN'); - --- --- Indeksy dla zrzutów tabel --- - --- --- Indeksy dla tabeli `exams` --- -ALTER TABLE `exams` - ADD PRIMARY KEY (`exam_id`), - ADD KEY `exam_type` (`exam_type_id`), - ADD KEY `exam_type_id` (`exam_type_id`); - --- --- Indeksy dla tabeli `exam_type` --- -ALTER TABLE `exam_type` - ADD PRIMARY KEY (`exam_type_id`); - --- --- Indeksy dla tabeli `general_group` --- -ALTER TABLE `general_group` - ADD PRIMARY KEY (`general_group_id`); - --- --- Indeksy dla tabeli `groups` --- -ALTER TABLE `groups` - ADD PRIMARY KEY (`group_id`), - ADD KEY `general_group` (`general_group_id`), - ADD KEY `general_group_id` (`general_group_id`); - --- --- Indeksy dla tabeli `otp_codes` --- -ALTER TABLE `otp_codes` - ADD PRIMARY KEY (`otp_code_id`), - ADD KEY `user_id` (`user_id`); - --- --- Indeksy dla tabeli `users` --- -ALTER TABLE `users` - ADD PRIMARY KEY (`user_id`), - ADD KEY `general_group_id` (`general_group_id`); - --- --- AUTO_INCREMENT dla zrzuconych tabel --- - --- --- AUTO_INCREMENT dla tabeli `exam_type` --- -ALTER TABLE `exam_type` - MODIFY `exam_type_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; - --- --- AUTO_INCREMENT dla tabeli `general_group` --- -ALTER TABLE `general_group` - MODIFY `general_group_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15; - --- --- AUTO_INCREMENT dla tabeli `groups` --- -ALTER TABLE `groups` - MODIFY `group_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9; - --- --- AUTO_INCREMENT dla tabeli `users` --- -ALTER TABLE `users` - MODIFY `user_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; - -- -- Ograniczenia dla zrzutów tabel -- --- --- Ograniczenia dla tabeli `exams` --- -ALTER TABLE `exams` - ADD CONSTRAINT `exams_ibfk_1` FOREIGN KEY (`exam_type_id`) REFERENCES `exam_type` (`exam_type_id`); - --- --- Ograniczenia dla tabeli `groups` --- -ALTER TABLE `groups` - ADD CONSTRAINT `groups_ibfk_1` FOREIGN KEY (`general_group_id`) REFERENCES `general_group` (`general_group_id`); - --- --- Ograniczenia dla tabeli `otp_codes` --- -ALTER TABLE `otp_codes` - ADD CONSTRAINT `otp_codes_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`); - -- -- Ograniczenia dla tabeli `users` -- ALTER TABLE `users` - ADD CONSTRAINT `users_ibfk_1` FOREIGN KEY (`general_group_id`) REFERENCES `general_group` (`general_group_id`); + ADD CONSTRAINT `users_ibfk_1` FOREIGN KEY (`general_group_id`) REFERENCES `general_group` (`general_group_id`) ON DELETE CASCADE; COMMIT; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; From b737fd505b58d3225f1f95273370a477d0cb05f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:16:32 +0200 Subject: [PATCH 25/37] Create application-prod.properties --- qodana.yaml | 31 +++++++++++++++++++ .../resources/application-prod.properties | 17 ++++++++++ 2 files changed, 48 insertions(+) create mode 100644 qodana.yaml create mode 100644 src/main/resources/application-prod.properties diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..bf26760 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,31 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +projectJDK: "21" #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-jvm:2025.1 diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..52e3721 --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,17 @@ +spring.datasource.url=jdbc:mysql://localhost:3306/pktt?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC +spring.datasource.username=pkttuser +spring.datasource.password=pkttpassword + +server.port=8080 +server.address=${SERVER_ADDRESS} + +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false +spring.jpa.hibernate.ddl-auto=none +spring.datasource.hikari.initialization-fail-timeout=0 + +logging.file.name=logs/app.log +logging.file.path=logs + +spring.cache.type=caffeine From 3f303dc382dbafb92e5184d939940ecc3472c82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:20:39 +0200 Subject: [PATCH 26/37] Update application-prod.properties --- src/main/resources/application-prod.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index 52e3721..567cfdb 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -3,7 +3,7 @@ spring.datasource.username=pkttuser spring.datasource.password=pkttpassword server.port=8080 -server.address=${SERVER_ADDRESS} +server.address=155.133.73.101 spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect From 7bee9f40148e5308b24759cd09d2bf70fa3d186a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:35:54 +0200 Subject: [PATCH 27/37] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f2676ca..68b6119 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,4 +7,4 @@ FROM amazoncorretto:21 WORKDIR /app COPY --from=build /app/target/*.jar app.jar EXPOSE 8080 -ENTRYPOINT ["java", "-jar", "app.jar"] +ENTRYPOINT ["java","-Dspring.profiles.active=prod", "-jar", "app.jar"] From 3aaa859c9e328e966800cee9fae9d6ba839d524d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Thu, 14 Aug 2025 23:31:49 +0200 Subject: [PATCH 28/37] Injection-proof solution --- .../pkwmtt/timetable/dto/DayOfWeekDTO.java | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java index 5ab6438..53d581b 100644 --- a/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java +++ b/src/main/java/org/pkwmtt/timetable/dto/DayOfWeekDTO.java @@ -12,28 +12,28 @@ public class DayOfWeekDTO { private final String name; private List odd; private List even; - - public DayOfWeekDTO(String name) { + + public DayOfWeekDTO (String name) { this.name = name; odd = new ArrayList<>(); even = new ArrayList<>(); } - - - public void add(SubjectDTO subjectDTO, boolean isNotOdd) { + + + public void add (SubjectDTO subjectDTO, boolean isNotOdd) { if (isNotOdd) { even.add(subjectDTO); } else { odd.add(subjectDTO); } } - - - public void deleteSubjectTypesFromNames() { + + + public void deleteSubjectTypesFromNames () { even.forEach(SubjectDTO::deleteTypeAndUnnecessaryCharactersFromName); odd.forEach(SubjectDTO::deleteTypeAndUnnecessaryCharactersFromName); } - + /** * Filters both odd- and even-week subject lists, * keeping only those entries that belong exclusively @@ -43,23 +43,24 @@ public void deleteSubjectTypesFromNames() { * where the first character is the group letter * and the last character is the subgroup number */ - public void filterByGroup(String group) { + public void filterByGroup (String group) { // Delete first character if group starts 'G' - if (group.charAt(0) == 'G' && group.length() > 3) + if (group.charAt(0) == 'G' && group.length() > 3) { group = group.substring(1); - + } + // Extract the group letter (e.g., "K" from "K03") var groupName = String.valueOf(group.charAt(0)); - + // Extract the subgroup digit (e.g., "3" from "K03") var targetNumber = String.valueOf(group.charAt(group.length() - 1)); - + // Apply the filter to both odd- and even-week lists odd = filter(odd, groupName, targetNumber); even = filter(even, groupName, targetNumber); - + } - + /** * Returns a new list containing only those SubjectDTO items * whose type string matches exclusively the target group code or doesn't have group at all. @@ -69,22 +70,16 @@ public void filterByGroup(String group) { * @param targetNumber the subgroup digit to keep (e.g., "3") * @return a filtered list of SubjectDTO */ - private List filter(List list, String groupName, String targetNumber) { - + private List filter (List list, String groupName, String targetNumber) { + list = list.stream() - // Keep only items that have no other subgroup codes - .filter( - item -> - hasOnlyTargetGroup( - item.getName(), - groupName, - targetNumber - ) - ).toList(); - + // Keep only items that have no other subgroup codes + .filter(item -> hasOnlyTargetGroup(item.getName(), groupName, targetNumber)) + .toList(); + return list; } - + /** * Checks if the given element string contains no other codes for the same group.* * @@ -93,15 +88,23 @@ private List filter(List list, String groupName, String * @param targetNumber the digit we want to allow (e.g., "3") * @return true if no non-target subgroup codes are present */ - private boolean hasOnlyTargetGroup(String element, String groupName, String targetNumber) { - var pattern = Pattern.compile(String.format("\\bG?[%s]0[1-9]\\b", groupName)); + private boolean hasOnlyTargetGroup (String element, String groupName, String targetNumber) { + var pattern = Pattern.compile(String.format( + "\\bG?[%s]0[1-9]\\b", + Pattern.quote(groupName) + )); var matcher = pattern.matcher(element); - if (!matcher.find()) + if (!matcher.find()) { return true; - - pattern = Pattern.compile(String.format("%s0%s", groupName, targetNumber)); + } + + pattern = Pattern.compile(String.format( + "%s0%s", + Pattern.quote(groupName), + Pattern.quote(targetNumber) + )); matcher = pattern.matcher(element); return matcher.find(); } - + } \ No newline at end of file From 54dc79610e2f9a0db1519df414aa81fff443c805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:03:47 +0200 Subject: [PATCH 29/37] Update application-prod.properties --- src/main/resources/application-prod.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index 567cfdb..cd609c7 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -3,7 +3,7 @@ spring.datasource.username=pkttuser spring.datasource.password=pkttpassword server.port=8080 -server.address=155.133.73.101 +server.address=0.0.0.0 spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect From 5cd3cfe83d3380ef93c960e7de1fef7ffee8697e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:50:47 +0200 Subject: [PATCH 30/37] Created Email service --- Dockerfile | 2 +- logs/app.log | 1337 ----------------- pom.xml | 15 +- .../java/org/pkwmtt/mail/EmailService.java | 33 + .../org/pkwmtt/mail/EmailTempController.java | 26 + .../org/pkwmtt/mail/config/MailConfig.java | 35 + .../java/org/pkwmtt/mail/dto/MailDTO.java | 14 + src/main/resources/application.properties | 9 + 8 files changed, 132 insertions(+), 1339 deletions(-) create mode 100644 src/main/java/org/pkwmtt/mail/EmailService.java create mode 100644 src/main/java/org/pkwmtt/mail/EmailTempController.java create mode 100644 src/main/java/org/pkwmtt/mail/config/MailConfig.java create mode 100644 src/main/java/org/pkwmtt/mail/dto/MailDTO.java diff --git a/Dockerfile b/Dockerfile index 68b6119..f2676ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,4 +7,4 @@ FROM amazoncorretto:21 WORKDIR /app COPY --from=build /app/target/*.jar app.jar EXPOSE 8080 -ENTRYPOINT ["java","-Dspring.profiles.active=prod", "-jar", "app.jar"] +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/logs/app.log b/logs/app.log index c19acaf..e69de29 100644 --- a/logs/app.log +++ b/logs/app.log @@ -1,1337 +0,0 @@ -2025-08-11 23:21:48 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-11 23:21:49 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-11 23:22:35 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-11 23:25:54 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-11 23:26:11 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:28:21 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:29:24 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:31:56 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:35:09 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:36:21 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.UnsupportedOperationException] with root cause -java.lang.UnsupportedOperationException: null - at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) - at java.base/java.util.ImmutableCollections$AbstractImmutableList.sort(ImmutableCollections.java:263) - at java.base/java.util.Collections.sort(Collections.java:145) - at org.pkwmtt.timetable.TimetableController.getListOfGeneralGroups(TimetableController.java:62) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-11 23:41:23 ERROR o.s.b.d.LoggingFailureAnalysisReporter - - -*************************** -APPLICATION FAILED TO START -*************************** - -Description: - -Parameter 0 of constructor in org.pkwmtt.temp.CacheInspectorController required a bean of type 'org.pkwmtt.timetable.TimetableService' that could not be found. - - -Action: - -Consider defining a bean of type 'org.pkwmtt.timetable.TimetableService' in your configuration. - -2025-08-11 23:58:48 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException: Specified general group doesn't exists] with root cause -org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException: Specified general group doesn't exists - at org.pkwmtt.timetable.TimetableService.getFilteredGeneralGroupSchedule(TimetableService.java:84) - at org.pkwmtt.timetable.TimetableController.getGeneralGroupSchedule(TimetableController.java:39) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) - at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) - at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) - at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) - at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) - at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) - at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) - at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) - at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) - at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) - at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) - at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) - at org.springframework.security.web.ObservationFilterChainDecorator$FilterObservation$SimpleFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:479) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$1(ObservationFilterChainDecorator.java:340) - at org.springframework.security.web.ObservationFilterChainDecorator.lambda$wrapSecured$0(ObservationFilterChainDecorator.java:82) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:128) - at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:101) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:125) - at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) - at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) - at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) - at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) - at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) - at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) - at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) - at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) - at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) - at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267) - at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) - at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) - at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240) - at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) - at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) - at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) - at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) - at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) - at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) - at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) - at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) - at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) - at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) - at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) - at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) - at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) - at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) - at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) - at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769) - at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) - at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) - at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) - at java.base/java.lang.Thread.run(Thread.java:1583) -2025-08-12 00:14:42 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-12 00:14:43 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-12 00:14:58 ERROR o.s.boot.SpringApplication - Application run failed -org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.pkwmtt.PkwmttBackendApplication] - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:194) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:418) - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) - at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) - at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) - at org.pkwmtt.PkwmttBackendApplication.main(PkwmttBackendApplication.java:12) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) -Caused by: java.lang.IllegalArgumentException: Could not find class [org.pkwmtt.timetable.TimetableController] - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:372) - at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:466) - at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:391) - at org.springframework.core.annotation.TypeMappedAnnotation.asMap(TypeMappedAnnotation.java:278) - at org.springframework.core.annotation.AbstractMergedAnnotation.asAnnotationAttributes(AbstractMergedAnnotation.java:191) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:147) - at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:113) - at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:281) - at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128) - at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:346) - at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:281) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:204) - at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:172) - ... 16 common frames omitted -Caused by: java.lang.ClassNotFoundException: org.pkwmtt.timetable.TimetableController - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) - at java.base/java.lang.Class.forName0(Native Method) - at java.base/java.lang.Class.forName(Class.java:534) - at java.base/java.lang.Class.forName(Class.java:513) - at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) - at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:362) - ... 28 common frames omitted -2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:15:10 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:16:07 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:16:42 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. -2025-08-12 00:16:56 ERROR o.p.t.TimetableExceptionHandler - SERVICE_UNAVAILABLE # Content of university webpage is not available right now. diff --git a/pom.xml b/pom.xml index 5b85e73..1ab2d6a 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,9 @@ PKWMTT-backend 0.1.0-ALPHA PKWMTT-backend - PKWM App (Server) – timetable, exam calendar and ECTS calculator for students of Mechanical Engineering @ Cracow University of Technology + PKWM App (Server) – timetable, exam calendar and ECTS calculator for students of Mechanical Engineering + @ Cracow University of Technology + https://github.com/PKTTTeam/PKWMTT-backend @@ -147,6 +149,17 @@ wiremock-spring-boot 3.10.0 + + + org.springframework.boot + spring-boot-starter-mail + + + + io.github.cdimascio + dotenv-java + 3.0.0 + diff --git a/src/main/java/org/pkwmtt/mail/EmailService.java b/src/main/java/org/pkwmtt/mail/EmailService.java new file mode 100644 index 0000000..13b8a14 --- /dev/null +++ b/src/main/java/org/pkwmtt/mail/EmailService.java @@ -0,0 +1,33 @@ +package org.pkwmtt.mail; + +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.mail.dto.MailDTO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class EmailService { + + private final JavaMailSender mailSender; + + @Value("${EMAIL_USERNAME}") + private String hostEmail; + + public void send (MailDTO mail) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setFrom(hostEmail); + helper.setTo(mail.getRecipient()); + helper.setText(mail.getDescription(), true); + helper.setSubject(mail.getTitle()); + + mailSender.send(message); + + } +} diff --git a/src/main/java/org/pkwmtt/mail/EmailTempController.java b/src/main/java/org/pkwmtt/mail/EmailTempController.java new file mode 100644 index 0000000..9898a23 --- /dev/null +++ b/src/main/java/org/pkwmtt/mail/EmailTempController.java @@ -0,0 +1,26 @@ +package org.pkwmtt.mail; + +import jakarta.mail.MessagingException; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.mail.dto.MailDTO; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/mail") +public class EmailTempController { + + private final EmailService service; + + @PostMapping + public void sendMail (@RequestParam(name = "r") String recipientEmailAddress) + throws MessagingException { + service.send(new MailDTO() + .setRecipient(recipientEmailAddress) + .setDescription("TEST") + .setTitle("TEST")); + } +} diff --git a/src/main/java/org/pkwmtt/mail/config/MailConfig.java b/src/main/java/org/pkwmtt/mail/config/MailConfig.java new file mode 100644 index 0000000..f70938f --- /dev/null +++ b/src/main/java/org/pkwmtt/mail/config/MailConfig.java @@ -0,0 +1,35 @@ +package org.pkwmtt.mail.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import java.util.Properties; + +@Configuration +public class MailConfig { + + @Value("${EMAIL_USERNAME}") + private String username; + @Value("${EMAIL_PASSWORD}") + private String password; + + @Bean + public JavaMailSender javaMailSender () { + + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setHost("smtp.gmail.com"); + mailSender.setPort(587); + mailSender.setUsername(username); + mailSender.setPassword(password); + + Properties props = mailSender.getJavaMailProperties(); + props.put("mail.transport.protocol", "smtp"); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + + return mailSender; + } +} diff --git a/src/main/java/org/pkwmtt/mail/dto/MailDTO.java b/src/main/java/org/pkwmtt/mail/dto/MailDTO.java new file mode 100644 index 0000000..215d774 --- /dev/null +++ b/src/main/java/org/pkwmtt/mail/dto/MailDTO.java @@ -0,0 +1,14 @@ +package org.pkwmtt.mail.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +public class MailDTO { + private String recipient; + private String title; + private String description; +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4c1aef9..af7d7de 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,6 @@ +#Import .env variables +spring.config.import=optional:file:.env[.properties] + spring.datasource.url=jdbc:mysql://localhost:3306/pktt?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC spring.datasource.username=pkttuser spring.datasource.password=pkttpassword @@ -18,3 +21,9 @@ spring.cache.type=caffeine logging.level.WireMock.my-mock=off +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.username=${EMAIL_USERNAME} +spring.mail.password=${EMAIL_PASSWORD} +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true From 3d856b02c2f90f7de87570357e8a610525756c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 15 Aug 2025 20:52:38 +0200 Subject: [PATCH 31/37] Update docker-image.yml --- .github/workflows/docker-image.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9ccf3e8..439bed2 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -108,3 +108,6 @@ jobs: ghcr.io/${{ env.IMAGE_REPO }}:${{ github.run_number }} cache-from: type=gha cache-to: type=gha,mode=max + build-args: | + EMAIL_USERNAME=${{ secrets.EMAIL_USERNAME }} + EMAIL_PASSWORD=${{ secrets.EMAIL_PASSWORD }} \ No newline at end of file From 4612a1e8b38c20f987ffca154561d099983d3fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 15 Aug 2025 21:05:36 +0200 Subject: [PATCH 32/37] Change value signature --- src/main/java/org/pkwmtt/mail/EmailService.java | 2 +- src/main/java/org/pkwmtt/mail/config/MailConfig.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/pkwmtt/mail/EmailService.java b/src/main/java/org/pkwmtt/mail/EmailService.java index 13b8a14..6d36660 100644 --- a/src/main/java/org/pkwmtt/mail/EmailService.java +++ b/src/main/java/org/pkwmtt/mail/EmailService.java @@ -15,7 +15,7 @@ public class EmailService { private final JavaMailSender mailSender; - @Value("${EMAIL_USERNAME}") + @Value("${spring.mail.username}") private String hostEmail; public void send (MailDTO mail) throws MessagingException { diff --git a/src/main/java/org/pkwmtt/mail/config/MailConfig.java b/src/main/java/org/pkwmtt/mail/config/MailConfig.java index f70938f..618dc08 100644 --- a/src/main/java/org/pkwmtt/mail/config/MailConfig.java +++ b/src/main/java/org/pkwmtt/mail/config/MailConfig.java @@ -11,9 +11,9 @@ @Configuration public class MailConfig { - @Value("${EMAIL_USERNAME}") + @Value("${spring.mail.username}") private String username; - @Value("${EMAIL_PASSWORD}") + @Value("${spring.mail.password}") private String password; @Bean From 367f1958dfb71a57d37706f9a5301efaf9df29da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Fri, 15 Aug 2025 21:08:54 +0200 Subject: [PATCH 33/37] Update docker-image.yml --- .github/workflows/docker-image.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 439bed2..ea94ec7 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -40,6 +40,8 @@ jobs: SPRING_DATASOURCE_USERNAME: ${{ secrets.SPRING_DATASOURCE_USERNAME }} SPRING_DATASOURCE_PASSWORD: ${{ secrets.SPRING_DATASOURCE_PASSWORD }} SPRING_PROFILES_ACTIVE: ${{ secrets.SPRING_PROFILES_ACTIVE }} + EMAIL_USERNAME: ${{ secrets.EMAIL_USERNAME }} + EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }} steps: - name: Checkout code From b832f3ca38b6fa6e457a8d6c28300ed44d6c6524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Sat, 16 Aug 2025 15:07:01 +0200 Subject: [PATCH 34/37] Detect if the mail server is available --- .../MailServiceNotAvailableException.java | 9 ++++++ .../java/org/pkwmtt/mail/EmailService.java | 19 ++++++++++-- .../org/pkwmtt/mail/EmailTempController.java | 19 ++++++++---- .../org/pkwmtt/mail/config/MailConfig.java | 30 ++++++++++++++++--- src/main/resources/application.properties | 4 +-- 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/pkwmtt/exceptions/MailServiceNotAvailableException.java diff --git a/src/main/java/org/pkwmtt/exceptions/MailServiceNotAvailableException.java b/src/main/java/org/pkwmtt/exceptions/MailServiceNotAvailableException.java new file mode 100644 index 0000000..e5f98de --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/MailServiceNotAvailableException.java @@ -0,0 +1,9 @@ +package org.pkwmtt.exceptions; + +public class MailServiceNotAvailableException + extends RuntimeException { + public MailServiceNotAvailableException () { + super("Mail service is not available right now."); + } + +} diff --git a/src/main/java/org/pkwmtt/mail/EmailService.java b/src/main/java/org/pkwmtt/mail/EmailService.java index 6d36660..1174ec0 100644 --- a/src/main/java/org/pkwmtt/mail/EmailService.java +++ b/src/main/java/org/pkwmtt/mail/EmailService.java @@ -1,10 +1,13 @@ package org.pkwmtt.mail; +import jakarta.annotation.PostConstruct; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.RequiredArgsConstructor; +import org.pkwmtt.exceptions.MailServiceNotAvailableException; +import org.pkwmtt.mail.config.MailConfig; import org.pkwmtt.mail.dto.MailDTO; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; @@ -12,13 +15,23 @@ @RequiredArgsConstructor @Service public class EmailService { + private final Environment environment; private final JavaMailSender mailSender; - @Value("${spring.mail.username}") private String hostEmail; - public void send (MailDTO mail) throws MessagingException { + @PostConstruct + private void assignProperties () { + hostEmail = environment.getProperty("spring.mail.username"); + } + + public void send (MailDTO mail) throws MessagingException, MailServiceNotAvailableException { + if (!MailConfig.isEnabled()) { + throw new MailServiceNotAvailableException(); + } + + MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); diff --git a/src/main/java/org/pkwmtt/mail/EmailTempController.java b/src/main/java/org/pkwmtt/mail/EmailTempController.java index 9898a23..1048a19 100644 --- a/src/main/java/org/pkwmtt/mail/EmailTempController.java +++ b/src/main/java/org/pkwmtt/mail/EmailTempController.java @@ -2,11 +2,12 @@ import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; +import org.pkwmtt.exceptions.MailServiceNotAvailableException; +import org.pkwmtt.exceptions.dto.ErrorResponseDTO; import org.pkwmtt.mail.dto.MailDTO; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -17,10 +18,18 @@ public class EmailTempController { @PostMapping public void sendMail (@RequestParam(name = "r") String recipientEmailAddress) - throws MessagingException { + throws MessagingException, MailServiceNotAvailableException { service.send(new MailDTO() .setRecipient(recipientEmailAddress) .setDescription("TEST") .setTitle("TEST")); } + + @ExceptionHandler(MailServiceNotAvailableException.class) + public ResponseEntity handle (Exception e) { + return new ResponseEntity<>( + new ErrorResponseDTO(e.getMessage()), + HttpStatus.SERVICE_UNAVAILABLE + ); + } } diff --git a/src/main/java/org/pkwmtt/mail/config/MailConfig.java b/src/main/java/org/pkwmtt/mail/config/MailConfig.java index 618dc08..595d744 100644 --- a/src/main/java/org/pkwmtt/mail/config/MailConfig.java +++ b/src/main/java/org/pkwmtt/mail/config/MailConfig.java @@ -1,25 +1,46 @@ package org.pkwmtt.mail.config; -import org.springframework.beans.factory.annotation.Value; +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import java.util.Properties; @Configuration +@RequiredArgsConstructor public class MailConfig { - @Value("${spring.mail.username}") + @Getter + private static boolean enabled = true; + + private final Environment environment; + private String username; - @Value("${spring.mail.password}") private String password; + @PostConstruct + private void assignAndValidateProperties () { + username = environment.getProperty("spring.mail.username"); + password = environment.getProperty("spring.mail.password"); + + if (username == null || password == null || username.isEmpty() || password.isEmpty()) { + enabled = false; + } + } + @Bean public JavaMailSender javaMailSender () { - JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + + if (!enabled) { + return mailSender; + } + mailSender.setHost("smtp.gmail.com"); mailSender.setPort(587); mailSender.setUsername(username); @@ -32,4 +53,5 @@ public JavaMailSender javaMailSender () { return mailSender; } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index af7d7de..a227ab6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,7 +23,7 @@ logging.level.WireMock.my-mock=off spring.mail.host=smtp.gmail.com spring.mail.port=587 -spring.mail.username=${EMAIL_USERNAME} -spring.mail.password=${EMAIL_PASSWORD} +spring.mail.username=${EMAIL_USERNAME:} +spring.mail.password=${EMAIL_PASSWORD:} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true From 9f247bc9f2f67a4605ddf743fe53a7e5d3c6e077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:59:03 +0200 Subject: [PATCH 35/37] Create status checker endpoint for service condition --- .../pkwmtt/status/DatabaseStatusChecker.java | 26 ++++++ .../status/SystemStatusCheckerService.java | 47 ++++++++++ .../pkwmtt/status/SystemStatusController.java | 20 ++++ .../timetable/TimetableCacheService.java | 93 +++++++++++-------- .../pkwmtt/timetable/TimetableService.java | 29 ++++-- 5 files changed, 166 insertions(+), 49 deletions(-) create mode 100644 src/main/java/org/pkwmtt/status/DatabaseStatusChecker.java create mode 100644 src/main/java/org/pkwmtt/status/SystemStatusCheckerService.java create mode 100644 src/main/java/org/pkwmtt/status/SystemStatusController.java diff --git a/src/main/java/org/pkwmtt/status/DatabaseStatusChecker.java b/src/main/java/org/pkwmtt/status/DatabaseStatusChecker.java new file mode 100644 index 0000000..6af41a8 --- /dev/null +++ b/src/main/java/org/pkwmtt/status/DatabaseStatusChecker.java @@ -0,0 +1,26 @@ +package org.pkwmtt.status; + + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; +import java.sql.SQLException; + +@Slf4j +@Service +public class DatabaseStatusChecker { + @Getter + private static boolean enabled = false; + + @Autowired + DatabaseStatusChecker (DataSource dataSource) { + try { + enabled = dataSource.getConnection().isValid(2); + } catch (SQLException e) { + log.error("Couldn't check database connection. Service will be unavailable"); + } + } +} diff --git a/src/main/java/org/pkwmtt/status/SystemStatusCheckerService.java b/src/main/java/org/pkwmtt/status/SystemStatusCheckerService.java new file mode 100644 index 0000000..e7a4731 --- /dev/null +++ b/src/main/java/org/pkwmtt/status/SystemStatusCheckerService.java @@ -0,0 +1,47 @@ +package org.pkwmtt.status; + +import jakarta.annotation.PostConstruct; +import org.pkwmtt.mail.config.MailConfig; +import org.pkwmtt.timetable.TimetableCacheService; +import org.pkwmtt.timetable.TimetableService; +import org.springframework.stereotype.Service; + + +@Service +public class SystemStatusCheckerService { + + private String mailingStatus; + private String databaseStatus; + private String cacheStatus; + private String timetableStatus; + + SystemStatusCheckerService () { + checkStatuses(); + } + + @PostConstruct + private void checkStatuses () { + mailingStatus = assignStatus(MailConfig.isEnabled()); + databaseStatus = assignStatus(DatabaseStatusChecker.isEnabled()); + timetableStatus = assignStatus(TimetableService.isEnabled()); + cacheStatus = assignStatus(TimetableCacheService.isCacheAvailable()); + } + + public String getStatus () { + return String.format( + """ + Server: ✅; + Services: + Mail: %s + Database: %s, + Timetable: %s, + Cache: %s + """, mailingStatus, databaseStatus, timetableStatus, cacheStatus + ); + } + + + private String assignStatus (boolean condition) { + return condition ? "✅" : "❌"; + } +} diff --git a/src/main/java/org/pkwmtt/status/SystemStatusController.java b/src/main/java/org/pkwmtt/status/SystemStatusController.java new file mode 100644 index 0000000..dd055c1 --- /dev/null +++ b/src/main/java/org/pkwmtt/status/SystemStatusController.java @@ -0,0 +1,20 @@ +package org.pkwmtt.status; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/pkwmtt/system/status") +@RequiredArgsConstructor +public class SystemStatusController { + private final SystemStatusCheckerService service; + + @GetMapping + public ResponseEntity getSystemStatus () { + return ResponseEntity.ok(service.getStatus()); + } + +} diff --git a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java index 09ad1d1..6bd4861 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableCacheService.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Getter; import org.jsoup.Jsoup; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; @@ -23,23 +24,37 @@ public class TimetableCacheService { private final TimetableParserService parser; private final ObjectMapper mapper; - private final Cache cache; - + + @Getter + private static boolean cacheAvailable = true; + @Value("${main.url:https://podzial.mech.pk.edu.pl/stacjonarne/html/}") private String mainUrl; - - public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) - throws IllegalAccessException { + + public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper, CacheManager cacheManager) { this.parser = parser; this.mapper = mapper; cache = cacheManager.getCache("timetables"); - + if (isNull(cache)) { - throw new IllegalAccessException("Cache [timetables] not configured"); + cacheAvailable = false; } } - + + /** + * @return connection status + */ + public static boolean isConnectionAvailable () { + try { + fetchData("https://podzial.mech.pk.edu.pl/stacjonarne/html/"); + return true; + } catch (Exception e) { + System.out.println(e.getMessage()); + return false; + } + } + /** * Fetches and parses the full timetable for a general group. * @@ -48,32 +63,30 @@ public TimetableCacheService (TimetableParserService parser, ObjectMapper mapper * @throws WebPageContentNotAvailableException if remote content is unavailable */ public TimetableDTO getGeneralGroupSchedule (String generalGroupName) - throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { + throws WebPageContentNotAvailableException, SpecifiedGeneralGroupDoesntExistsException { var generalGroupList = getGeneralGroupsMap(); - + if (!generalGroupList.containsKey(generalGroupName)) { throw new SpecifiedGeneralGroupDoesntExistsException(generalGroupName); } - + String groupUrl = generalGroupList.get(generalGroupName); String url = mainUrl + groupUrl; String cacheKey = "timetable_" + generalGroupName; var html = fetchData(url); String json = cache.get( - cacheKey, - () -> { - var timetableDTO =new TimetableDTO( - generalGroupName, - parser.parse(html)); - return mapper.writeValueAsString(timetableDTO); - } + cacheKey, () -> { + var timetableDTO = new TimetableDTO(generalGroupName, parser.parse(html)); + return mapper.writeValueAsString(timetableDTO); + } ); - + return getMappedValue( - json, cacheKey, cache, new TypeReference<>() {} + json, cacheKey, cache, new TypeReference<>() { + } ); } - + /** * Retrieves a mapping of general group names to their corresponding timetable URLs. * @@ -84,40 +97,40 @@ public Map getGeneralGroupsMap () throws WebPageContentNotAvaila var url = mainUrl + "lista.html"; var html = fetchData(url); String json = cache.get( - "generalGroupMap", - () -> mapper.writeValueAsString(parser.parseGeneralGroups(html)) + "generalGroupMap", + () -> mapper.writeValueAsString(parser.parseGeneralGroups(html)) ); - + return getMappedValue( - json, "generalGroupList", cache, new TypeReference<>() { - } + json, "generalGroupList", cache, new TypeReference<>() { + } ); } - + /** * Retrieves the standard list of hour ranges used in the timetable. * * @return list of hour labels (e.g., 08:00–09:30) * @throws WebPageContentNotAvailableException if hour definition page can't be loaded */ - public List getListOfHours () throws WebPageContentNotAvailableException { + public List getListOfHours () throws WebPageContentNotAvailableException { String url = mainUrl + "plany/o25.html"; String json = cache.get( - "hourList", - () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) + "hourList", + () -> mapper.writeValueAsString(parser.parseHours(fetchData(url))) ); - + List result = getMappedValue( - json, "hourList", cache, new TypeReference<>() { - } + json, "hourList", cache, new TypeReference<>() { + } ); - + //Delete useless spaces result = result.stream().map(item -> item.replaceAll(" ", "")).toList(); - + return result; } - + /** * @param json - json representation of java object * @param key - cache key @@ -128,7 +141,7 @@ public List getListOfHours () throws WebPageContentNotAvailableExceptio * @throws WebPageContentNotAvailableException if there were trouble with fetching data */ private T getMappedValue (String json, String key, Cache cache, TypeReference targetClass) - throws WebPageContentNotAvailableException { + throws WebPageContentNotAvailableException { try { return mapper.readValue(json, targetClass); } catch (JsonProcessingException e) { @@ -136,18 +149,18 @@ private T getMappedValue (String json, String key, Cache cache, TypeReferenc throw new WebPageContentNotAvailableException(); } } - + /** * @param url - url of webpage * @return html code of selected webpage * @throws WebPageContentNotAvailableException if there were trouble with fetching data */ - private String fetchData (String url) throws WebPageContentNotAvailableException { + private static String fetchData (String url) throws WebPageContentNotAvailableException { try { return Jsoup.connect(url).get().html(); } catch (IOException ioe) { throw new WebPageContentNotAvailableException(); } } - + } diff --git a/src/main/java/org/pkwmtt/timetable/TimetableService.java b/src/main/java/org/pkwmtt/timetable/TimetableService.java index c0e6ca3..849cd66 100644 --- a/src/main/java/org/pkwmtt/timetable/TimetableService.java +++ b/src/main/java/org/pkwmtt/timetable/TimetableService.java @@ -2,26 +2,36 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.pkwmtt.exceptions.SpecifiedGeneralGroupDoesntExistsException; import org.pkwmtt.exceptions.SpecifiedSubGroupDoesntExistsException; import org.pkwmtt.exceptions.WebPageContentNotAvailableException; import org.pkwmtt.timetable.dto.DayOfWeekDTO; import org.pkwmtt.timetable.dto.TimetableDTO; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @Slf4j @Service -@RequiredArgsConstructor public class TimetableService { private final TimetableCacheService cachedService; + @Getter + private static final boolean enabled = TimetableCacheService.isConnectionAvailable(); + + @Autowired + TimetableService (TimetableCacheService cachedService) { + this.cachedService = cachedService; + } + /** * Parses the timetable JSON to extract subgroup identifiers like K01, P03, GL04 using regex. * @@ -57,9 +67,7 @@ public List getAvailableSubGroups (String generalGroupName) matchedGroups.add(text); } - return matchedGroups.stream() - .sorted() - .toList(); + return matchedGroups.stream().sorted().toList(); } @@ -103,9 +111,12 @@ public TimetableDTO getFilteredGeneralGroupSchedule (String generalGroupName, Li * @return List of general group's names */ public List getGeneralGroupList () throws WebPageContentNotAvailableException { - return cachedService.getGeneralGroupsMap().keySet().stream() - .sorted() - .collect(Collectors.toList()); + return cachedService + .getGeneralGroupsMap() + .keySet() + .stream() + .sorted() + .collect(Collectors.toList()); } } From a4309e56d9080710b49407280a70a732c5725fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Piotrkowski?= Date: Mon, 18 Aug 2025 21:04:01 +0200 Subject: [PATCH 36/37] fix: update new init.sql --- init.sql | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 321 insertions(+), 6 deletions(-) diff --git a/init.sql b/init.sql index d31ad78..7d91d29 100644 --- a/init.sql +++ b/init.sql @@ -3,7 +3,7 @@ -- https://www.phpmyadmin.net/ -- -- Host: db --- Generation Time: Aug 13, 2025 at 08:18 PM +-- Generation Time: Aug 18, 2025 at 07:00 PM -- Wersja serwera: 9.3.0 -- Wersja PHP: 8.2.27 @@ -20,6 +20,192 @@ SET time_zone = "+00:00"; -- -- Baza danych: `pktt` -- +CREATE DATABASE IF NOT EXISTS `pktt` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE `pktt`; + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `exams` +-- + +DROP TABLE IF EXISTS `exams`; +CREATE TABLE `exams` ( + `exam_id` int NOT NULL, + `title` varchar(255) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `exam_date` datetime NOT NULL, + `exam_type_id` int NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `exams` +-- + +TRUNCATE TABLE `exams`; +-- +-- Zrzut danych tabeli `exams` +-- + +INSERT INTO `exams` (`exam_id`, `title`, `description`, `exam_date`, `exam_type_id`) VALUES +(1, 'Kolokwium z matematyki', 'Pierwsze kolokwium obejmujące rozdziały 1–3', '2025-10-01 10:00:00', 1), +(2, 'Egzamin końcowy z programowania', 'Egzamin pisemny i praktyczny', '2025-01-20 09:00:00', 2), +(3, 'Projekt z baz danych', 'Oddanie projektu grupowego', '2025-06-15 23:59:00', 3), +(4, 'Kolokwium z fizyki', 'Druga część materiału: mechanika', '2025-11-05 12:00:00', 1), +(5, 'Egzamin końcowy z ekonomii', 'Egzamin pisemny testowy', '2025-02-10 08:30:00', 2), +(6, 'Projekt z systemów operacyjnych', 'Prezentacja projektu semestralnego', '2025-06-25 14:00:00', 3); + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `exams_groups` +-- + +DROP TABLE IF EXISTS `exams_groups`; +CREATE TABLE `exams_groups` ( + `exam_group_id` int NOT NULL, + `exam_id` int NOT NULL, + `group_id` int NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `exams_groups` +-- + +TRUNCATE TABLE `exams_groups`; +-- +-- Zrzut danych tabeli `exams_groups` +-- + +INSERT INTO `exams_groups` (`exam_group_id`, `exam_id`, `group_id`) VALUES +(7, 1, 9), +(8, 1, 10), +(9, 2, 12), +(10, 2, 13), +(11, 2, 14), +(12, 3, 15), +(13, 3, 16), +(14, 3, 17), +(15, 4, 9), +(16, 4, 10), +(17, 5, 12), +(18, 5, 13), +(19, 6, 15), +(20, 6, 16); + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `exam_type` +-- + +DROP TABLE IF EXISTS `exam_type`; +CREATE TABLE `exam_type` ( + `exam_type_id` int NOT NULL, + `name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `exam_type` +-- + +TRUNCATE TABLE `exam_type`; +-- +-- Zrzut danych tabeli `exam_type` +-- + +INSERT INTO `exam_type` (`exam_type_id`, `name`) VALUES +(1, 'Kolokwium'), +(2, 'Egzamin końcowy'), +(3, 'Projekt'); + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `general_group` +-- + +DROP TABLE IF EXISTS `general_group`; +CREATE TABLE `general_group` ( + `general_group_id` int NOT NULL, + `name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `general_group` +-- + +TRUNCATE TABLE `general_group`; +-- +-- Zrzut danych tabeli `general_group` +-- + +INSERT INTO `general_group` (`general_group_id`, `name`) VALUES +(17, '11A'), +(18, '12E'), +(19, '13K'), +(20, '14M'); + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `groups` +-- + +DROP TABLE IF EXISTS `groups`; +CREATE TABLE `groups` ( + `group_id` int NOT NULL, + `name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `groups` +-- + +TRUNCATE TABLE `groups`; +-- +-- Zrzut danych tabeli `groups` +-- + +INSERT INTO `groups` (`group_id`, `name`) VALUES +(9, '11A1'), +(10, '11A2'), +(12, '12E1'), +(13, '12E2'), +(14, '12E3'), +(15, '13K1'), +(16, '13K2'), +(17, '13K3'), +(18, '14M1'); + +-- -------------------------------------------------------- + +-- +-- Struktura tabeli dla tabeli `otp_codes` +-- + +DROP TABLE IF EXISTS `otp_codes`; +CREATE TABLE `otp_codes` ( + `otp_code_id` int NOT NULL, + `code` varchar(255) NOT NULL, + `expire` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `general_group_id` int NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- Tabela Truncate przed wstawieniem `otp_codes` +-- + +TRUNCATE TABLE `otp_codes`; +-- +-- Zrzut danych tabeli `otp_codes` +-- + +INSERT INTO `otp_codes` (`otp_code_id`, `code`, `expire`, `general_group_id`) VALUES +(1, 'ABC123', '2025-08-18 19:51:40', 17), +(2, 'XYZ789', '2025-08-18 20:51:40', 18), +(3, 'QWE456', '2025-08-18 21:51:40', 19), +(4, 'JKL999', '2025-08-18 22:51:40', 20); -- -------------------------------------------------------- @@ -28,20 +214,149 @@ SET time_zone = "+00:00"; -- DROP TABLE IF EXISTS `users`; -CREATE TABLE IF NOT EXISTS `users` ( - `user_id` int NOT NULL AUTO_INCREMENT, +CREATE TABLE `users` ( + `user_id` int NOT NULL, `general_group_id` int NOT NULL, `email` varchar(255) NOT NULL, `is_active` tinyint(1) NOT NULL DEFAULT '1', - `role` enum('ADMIN','REPRESENTATIVE') NOT NULL DEFAULT 'REPRESENTATIVE', - PRIMARY KEY (`user_id`), - KEY `general_group_id_idx` (`general_group_id`) + `role` enum('ADMIN','REPRESENTATIVE') NOT NULL DEFAULT 'REPRESENTATIVE' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +-- +-- Tabela Truncate przed wstawieniem `users` +-- + +TRUNCATE TABLE `users`; +-- +-- Zrzut danych tabeli `users` +-- + +INSERT INTO `users` (`user_id`, `general_group_id`, `email`, `is_active`, `role`) VALUES +(1, 17, 'user11a@example.com', 1, 'REPRESENTATIVE'), +(2, 18, 'user12e@example.com', 1, 'REPRESENTATIVE'), +(3, 19, 'user13k@example.com', 1, 'REPRESENTATIVE'), +(4, 20, 'user14m@example.com', 1, 'ADMIN'); + +-- +-- Indeksy dla zrzutów tabel +-- + +-- +-- Indeksy dla tabeli `exams` +-- +ALTER TABLE `exams` + ADD PRIMARY KEY (`exam_id`), + ADD KEY `exam_type_id_idx` (`exam_type_id`); + +-- +-- Indeksy dla tabeli `exams_groups` +-- +ALTER TABLE `exams_groups` + ADD PRIMARY KEY (`exam_group_id`), + ADD KEY `exam_id_idx` (`exam_id`), + ADD KEY `group_id_idx` (`group_id`); + +-- +-- Indeksy dla tabeli `exam_type` +-- +ALTER TABLE `exam_type` + ADD PRIMARY KEY (`exam_type_id`); + +-- +-- Indeksy dla tabeli `general_group` +-- +ALTER TABLE `general_group` + ADD PRIMARY KEY (`general_group_id`); + +-- +-- Indeksy dla tabeli `groups` +-- +ALTER TABLE `groups` + ADD PRIMARY KEY (`group_id`); + +-- +-- Indeksy dla tabeli `otp_codes` +-- +ALTER TABLE `otp_codes` + ADD PRIMARY KEY (`otp_code_id`), + ADD KEY `general_group_id_idx` (`general_group_id`); + +-- +-- Indeksy dla tabeli `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`user_id`), + ADD KEY `general_group_id_idx` (`general_group_id`); + +-- +-- AUTO_INCREMENT dla zrzuconych tabel +-- + +-- +-- AUTO_INCREMENT dla tabeli `exams` +-- +ALTER TABLE `exams` + MODIFY `exam_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; + +-- +-- AUTO_INCREMENT dla tabeli `exams_groups` +-- +ALTER TABLE `exams_groups` + MODIFY `exam_group_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21; + +-- +-- AUTO_INCREMENT dla tabeli `exam_type` +-- +ALTER TABLE `exam_type` + MODIFY `exam_type_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; + +-- +-- AUTO_INCREMENT dla tabeli `general_group` +-- +ALTER TABLE `general_group` + MODIFY `general_group_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21; + +-- +-- AUTO_INCREMENT dla tabeli `groups` +-- +ALTER TABLE `groups` + MODIFY `group_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21; + +-- +-- AUTO_INCREMENT dla tabeli `otp_codes` +-- +ALTER TABLE `otp_codes` + MODIFY `otp_code_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; + +-- +-- AUTO_INCREMENT dla tabeli `users` +-- +ALTER TABLE `users` + MODIFY `user_id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; + -- -- Ograniczenia dla zrzutów tabel -- +-- +-- Ograniczenia dla tabeli `exams` +-- +ALTER TABLE `exams` + ADD CONSTRAINT `exams_ibfk_1` FOREIGN KEY (`exam_type_id`) REFERENCES `exam_type` (`exam_type_id`) ON DELETE CASCADE; + +-- +-- Ograniczenia dla tabeli `exams_groups` +-- +ALTER TABLE `exams_groups` + ADD CONSTRAINT `exams_groups_ibfk_1` FOREIGN KEY (`exam_id`) REFERENCES `exams` (`exam_id`) ON DELETE CASCADE, + ADD CONSTRAINT `exams_groups_ibfk_2` FOREIGN KEY (`group_id`) REFERENCES `groups` (`group_id`) ON DELETE CASCADE; + +-- +-- Ograniczenia dla tabeli `otp_codes` +-- +ALTER TABLE `otp_codes` + ADD CONSTRAINT `otp_codes_ibfk_1` FOREIGN KEY (`general_group_id`) REFERENCES `general_group` (`general_group_id`) ON DELETE CASCADE; + -- -- Ograniczenia dla tabeli `users` -- From 2eb3b96efbde8806ef06a9a81ec7b9a5b31f5e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florczak?= <84631301+florczaq@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:59:29 +0200 Subject: [PATCH 37/37] Resolve conficlts --- pom.xml | 365 +++++------ src/main/java/org/pkwmtt/entity/Exam.java | 38 -- src/main/java/org/pkwmtt/entity/ExamType.java | 18 - .../java/org/pkwmtt/entity/GeneralGroup.java | 17 - src/main/java/org/pkwmtt/entity/OTPCode.java | 25 - .../java/org/pkwmtt/entity/StudentGroup.java | 23 - src/main/java/org/pkwmtt/entity/User.java | 29 - .../pkwmtt/examCalendar/ExamController.java | 90 +++ .../examCalendar/ExamControllerAdvice.java | 48 ++ .../org/pkwmtt/examCalendar/ExamService.java | 86 +++ .../org/pkwmtt/examCalendar/dto/ExamDto.java | 33 + .../org/pkwmtt/examCalendar/entity/Exam.java | 48 ++ .../pkwmtt/examCalendar/entity/ExamType.java | 21 + .../examCalendar/entity/GeneralGroup.java | 26 + .../org/pkwmtt/examCalendar/entity/Group.java | 27 + .../pkwmtt/examCalendar/entity/OTPCode.java | 31 + .../org/pkwmtt/examCalendar/entity/User.java | 33 + .../mapper/ExamDtoToExamMapper.java | 49 ++ .../repository/ExamRepository.java | 74 +++ .../repository/ExamTypeRepository.java | 10 + .../repository/GeneralGroupRepository.java | 4 +- .../repository/GroupRepository.java | 7 + .../repository/OTPCodeRepository.java | 4 +- .../repository/UserRepository.java | 4 +- .../ExamTypeNotExistsException.java | 7 + .../InvalidGroupIdentifierException.java | 7 + .../NoSuchElementWithProvidedIdException.java | 7 + .../UnsupportedCountOfArgumentsException.java | 8 + .../org/pkwmtt/repository/ExamRepository.java | 7 - .../pkwmtt/repository/ExamTypeRepository.java | 7 - .../pkwmtt/repository/GroupRepository.java | 7 - .../examCalendar/ExamControllerTest.java | 620 ++++++++++++++++++ .../pkwmtt/examCalendar/ExamServiceTest.java | 331 ++++++++++ .../pkwmtt/examCalendar/dto/ExamDtoTest.java | 150 +++++ .../mapper/ExamDtoToExamMapperTest.java | 150 +++++ .../repository/ExamRepositoryTest.java | 182 +++++ src/test/resources/application.properties | 13 + src/test/resources/schema.sql | 60 ++ 38 files changed, 2299 insertions(+), 367 deletions(-) delete mode 100644 src/main/java/org/pkwmtt/entity/Exam.java delete mode 100644 src/main/java/org/pkwmtt/entity/ExamType.java delete mode 100644 src/main/java/org/pkwmtt/entity/GeneralGroup.java delete mode 100644 src/main/java/org/pkwmtt/entity/OTPCode.java delete mode 100644 src/main/java/org/pkwmtt/entity/StudentGroup.java delete mode 100644 src/main/java/org/pkwmtt/entity/User.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/ExamController.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/ExamControllerAdvice.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/ExamService.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/dto/ExamDto.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/Exam.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/ExamType.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/GeneralGroup.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/Group.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/OTPCode.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/entity/User.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapper.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/repository/ExamRepository.java create mode 100644 src/main/java/org/pkwmtt/examCalendar/repository/ExamTypeRepository.java rename src/main/java/org/pkwmtt/{ => examCalendar}/repository/GeneralGroupRepository.java (61%) create mode 100644 src/main/java/org/pkwmtt/examCalendar/repository/GroupRepository.java rename src/main/java/org/pkwmtt/{ => examCalendar}/repository/OTPCodeRepository.java (60%) rename src/main/java/org/pkwmtt/{ => examCalendar}/repository/UserRepository.java (60%) create mode 100644 src/main/java/org/pkwmtt/exceptions/ExamTypeNotExistsException.java create mode 100644 src/main/java/org/pkwmtt/exceptions/InvalidGroupIdentifierException.java create mode 100644 src/main/java/org/pkwmtt/exceptions/NoSuchElementWithProvidedIdException.java create mode 100644 src/main/java/org/pkwmtt/exceptions/UnsupportedCountOfArgumentsException.java delete mode 100644 src/main/java/org/pkwmtt/repository/ExamRepository.java delete mode 100644 src/main/java/org/pkwmtt/repository/ExamTypeRepository.java delete mode 100644 src/main/java/org/pkwmtt/repository/GroupRepository.java create mode 100644 src/test/java/org/pkwmtt/examCalendar/ExamControllerTest.java create mode 100644 src/test/java/org/pkwmtt/examCalendar/ExamServiceTest.java create mode 100644 src/test/java/org/pkwmtt/examCalendar/dto/ExamDtoTest.java create mode 100644 src/test/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapperTest.java create mode 100644 src/test/java/org/pkwmtt/examCalendar/repository/ExamRepositoryTest.java create mode 100644 src/test/resources/application.properties create mode 100644 src/test/resources/schema.sql diff --git a/pom.xml b/pom.xml index 1ab2d6a..1e54dfb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,203 +1,188 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.5.3 - - - org.pkwmtt - PKWMTT-backend - 0.1.0-ALPHA - PKWMTT-backend - PKWM App (Server) – timetable, exam calendar and ECTS calculator for students of Mechanical Engineering - @ Cracow University of Technology - - https://github.com/PKTTTeam/PKWMTT-backend - - - MIT - - - - - Mikołaj Florczak - https://github.com/florczaq - PKWM Mobile App Team - https://github.com/PKTTTeam - - - - - - - - - - - - 21 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.3 + + + org.pkwmtt + PKWMTT-backend + 0.0.1-SNAPSHOT + PKWMTT-backend + PKWMTT-backend + + + + + + + + + + + + + + + 21 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-jpa + - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-data-jpa - + + + org.projectlombok + lombok + true + - - - org.projectlombok - lombok - true - + + + + + + + - - - com.h2database - h2 - runtime - + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + + + + + junit + junit + 4.13.1 + + + + + + + + + org.mockito + mockito-core + 5.18.0 + - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.security - spring-security-test - test - - - - junit - junit - 4.13.1 - - - org.mockito - mockito-all - 1.10.19 - - - org.mockito - mockito-core - 5.18.0 - + + + com.h2database + h2 + test + - - - org.jsoup - jsoup - 1.15.3 - + + + org.jsoup + jsoup + 1.15.3 + - - - com.github.ben-manes.caffeine - caffeine - 3.1.8 - - - org.springframework.boot - spring-boot-starter-cache - + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + + org.springframework.boot + spring-boot-starter-cache + - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.8.9 - + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.6.0 + + + com.mysql + mysql-connector-j + runtime + + + + + com.mysql + mysql-connector-j + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-validation + - - - com.mysql - mysql-connector-j - - - org.springframework.boot - spring-boot-starter-actuator - - - - ch.qos.logback - logback-classic - 1.5.13 - - - org.wiremock.integrations - wiremock-spring-boot - 3.10.0 - - - - org.springframework.boot - spring-boot-starter-mail - - - - io.github.cdimascio - dotenv-java - 3.0.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - -javaagent:${settings.localRepository}/org/mockito/mockito-core/5.18.0/mockito-core-5.18.0.jar - - - + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -javaagent:${settings.localRepository}/org/mockito/mockito-core/5.18.0/mockito-core-5.18.0.jar + + - - + + diff --git a/src/main/java/org/pkwmtt/entity/Exam.java b/src/main/java/org/pkwmtt/entity/Exam.java deleted file mode 100644 index 6cda359..0000000 --- a/src/main/java/org/pkwmtt/entity/Exam.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; -import java.time.LocalDateTime; -import java.util.HashSet; -import java.util.Set; - -@Entity -@Table(name = "`exams`") -@Data -public class Exam { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "exam_id") - private Integer examId; - - @Column(nullable = false) - private String title; - - private String description; - - @Column(name = "`exam_date`", nullable = false) - private LocalDateTime examDate; - - @ManyToOne - @JoinColumn(name = "exam_type_id", nullable = false) - private ExamType examType; - - @ManyToMany - @JoinTable( - name="exams_groups", - joinColumns = @JoinColumn(name = "exam_id"), - inverseJoinColumns = @JoinColumn(name = "group_id") - ) - private Set groups = new HashSet<>();; - -} diff --git a/src/main/java/org/pkwmtt/entity/ExamType.java b/src/main/java/org/pkwmtt/entity/ExamType.java deleted file mode 100644 index fd971ef..0000000 --- a/src/main/java/org/pkwmtt/entity/ExamType.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; - -@Entity -@Data -@Table(name = "`exam_type`") -public class ExamType { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "exam_type_id", nullable = false) - private Integer examTypeId; - - @Column(nullable = false) - private String name; -} diff --git a/src/main/java/org/pkwmtt/entity/GeneralGroup.java b/src/main/java/org/pkwmtt/entity/GeneralGroup.java deleted file mode 100644 index eb00158..0000000 --- a/src/main/java/org/pkwmtt/entity/GeneralGroup.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; - -@Entity -@Data -@Table(name = "`general_group`") -public class GeneralGroup { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "general_group_id") - private Integer generalGroupId; - - @Column(nullable = false) - private String name; -} diff --git a/src/main/java/org/pkwmtt/entity/OTPCode.java b/src/main/java/org/pkwmtt/entity/OTPCode.java deleted file mode 100644 index a202b42..0000000 --- a/src/main/java/org/pkwmtt/entity/OTPCode.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; -import java.time.LocalDateTime; - -@Entity -@Data -@Table(name = "otp_codes") -public class OTPCode { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "otp_code_id") - private Integer otpCodeId; - - @Column(nullable = false) - private String code; - - @Column(nullable = false) - private LocalDateTime expire; - - @OneToOne - @JoinColumn(name = "`general_group_id`", nullable = false) - private GeneralGroup generalGroup; -} diff --git a/src/main/java/org/pkwmtt/entity/StudentGroup.java b/src/main/java/org/pkwmtt/entity/StudentGroup.java deleted file mode 100644 index 5c0f10c..0000000 --- a/src/main/java/org/pkwmtt/entity/StudentGroup.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; - -import java.util.HashSet; -import java.util.Set; - -@Entity -@Data -@Table(name = "`groups`") -public class StudentGroup { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "group_id") - private Integer groupId; - - @Column(nullable = false) - private String name; - - @ManyToMany(mappedBy = "groups") - private Set exams = new HashSet<>(); -} diff --git a/src/main/java/org/pkwmtt/entity/User.java b/src/main/java/org/pkwmtt/entity/User.java deleted file mode 100644 index 81a03c8..0000000 --- a/src/main/java/org/pkwmtt/entity/User.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.pkwmtt.entity; - -import jakarta.persistence.*; -import lombok.Data; -import org.pkwmtt.enums.Role; - -@Entity -@Data -@Table(name = "`users`") -public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "user_id") - private Integer userId; - - @OneToOne - @JoinColumn(name = "general_group_id", nullable = false) - private GeneralGroup generalGroup; - - @Column(nullable = false) - private String email; - - @Column(name = "is_active", nullable = false) - private boolean isActive; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private Role role; -} diff --git a/src/main/java/org/pkwmtt/examCalendar/ExamController.java b/src/main/java/org/pkwmtt/examCalendar/ExamController.java new file mode 100644 index 0000000..7c69cff --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/ExamController.java @@ -0,0 +1,90 @@ +package org.pkwmtt.examCalendar; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; +import java.util.List; +import java.util.Set; + +@Validated +@RequiredArgsConstructor +@RequestMapping("/pkwmtt/api/v1/exams") +@RestController +public class ExamController { + + private final ExamService examService; + + /** + * @param examDto details of exam + * @return 201 created with URI to GET method which returns created resource + */ + @PostMapping("") + public ResponseEntity addExam(@RequestBody @Valid ExamDto examDto) { + int id = examService.addExam(examDto); + URI uri = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(id) + .toUri(); + return ResponseEntity.created(uri).build(); +// TODO: test not null validation in controller + } + + /** + * @param id of exam or test + * @param examDto new details of exam or test + * @return 204 no content + */ + @PutMapping("/{id}") + public ResponseEntity modifyExam(@PathVariable @Positive int id, @RequestBody @Valid ExamDto examDto) { + examService.modifyExam(examDto, id); + return ResponseEntity.noContent().build(); + } + + /** + * @param id of exam or test + * @return 204 no content + */ + @DeleteMapping("/{id}") + public ResponseEntity deleteExam(@PathVariable int id) { + examService.deleteExam(id); + return ResponseEntity.noContent().build(); + } + + /** + * @param id of exam or test + * @return 200 ok with single exam or test details + */ + @GetMapping("/{id}") + public ResponseEntity getExam(@PathVariable int id) { + return ResponseEntity.ok(examService.getExamById(id)); + } + + /** + * @param groups set of groups + * @return 200 ok with list of exams for specific group + */ + @GetMapping("/by-groups") + public ResponseEntity> getExams(@RequestParam Set groups){ + return ResponseEntity.ok(examService.getExamByGroup(groups)); + } + + /** + * @return 200 ok with list of available exam types + */ +// should be moved to new controller? + @GetMapping("/exam-types") + public ResponseEntity> getExamTypes(){ + return ResponseEntity.ok(examService.getExamTypes()); + } + +} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/examCalendar/ExamControllerAdvice.java b/src/main/java/org/pkwmtt/examCalendar/ExamControllerAdvice.java new file mode 100644 index 0000000..845be7a --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/ExamControllerAdvice.java @@ -0,0 +1,48 @@ +package org.pkwmtt.examCalendar; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import org.pkwmtt.exceptions.ErrorResponseDTO; +import org.pkwmtt.exceptions.ExamTypeNotExistsException; +import org.pkwmtt.exceptions.NoSuchElementWithProvidedIdException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.stream.Collectors; + +@RestControllerAdvice +public class ExamControllerAdvice { + +// TODO: handle or remove UnsupportedCountOfArgumentsException + + @ExceptionHandler(NoSuchElementWithProvidedIdException.class) + public ResponseEntity handleNoSuchElementWithProvidedIdException(NoSuchElementWithProvidedIdException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponseDTO(e.getMessage())); + } + + @ExceptionHandler(ExamTypeNotExistsException.class) + public ResponseEntity handleExamTypeNotExistsException(ExamTypeNotExistsException e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponseDTO(e.getMessage())); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + String message = e.getBindingResult().getFieldErrors().stream() + .map(field -> field.getField() + " : " + field.getDefaultMessage()) + .collect(Collectors.joining(", ")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponseDTO(message)); + } + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) { + String message = e.getConstraintViolations().stream() + .map(field -> field.getPropertyPath() + " : " + field.getMessage()) + .collect(Collectors.joining(", ")); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ErrorResponseDTO(message)); + } + + +} diff --git a/src/main/java/org/pkwmtt/examCalendar/ExamService.java b/src/main/java/org/pkwmtt/examCalendar/ExamService.java new file mode 100644 index 0000000..8da2396 --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/ExamService.java @@ -0,0 +1,86 @@ +package org.pkwmtt.examCalendar; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.pkwmtt.examCalendar.mapper.ExamDtoToExamMapper; +import org.pkwmtt.examCalendar.repository.ExamRepository; +import org.pkwmtt.examCalendar.repository.ExamTypeRepository; +import org.pkwmtt.exceptions.NoSuchElementWithProvidedIdException; +import org.pkwmtt.exceptions.UnsupportedCountOfArgumentsException; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@RequiredArgsConstructor +@Transactional +public class ExamService { + + private final ExamRepository examRepository; + private final ExamDtoToExamMapper examMapper; + private final ExamTypeRepository examTypeRepository; + + /** + * @param examDto details of exam + * @return id of exam added to database + */ + public int addExam(ExamDto examDto) { + return examRepository.save(examMapper.mapToNewExam(examDto)).getExamId(); + } + + /** + * @param examDto new details of exam that overwrite old ones + * @param id of exam that need to be modified + */ + public void modifyExam(ExamDto examDto, int id) { + examRepository.findById(id).orElseThrow(() -> new NoSuchElementWithProvidedIdException(id)); + examRepository.save(examMapper.mapToExistingExam(examDto, id)); + } + + /** + * @param id of exam + */ + public void deleteExam(int id) { + examRepository.findById(id).orElseThrow(() -> new NoSuchElementWithProvidedIdException(id)); + examRepository.deleteById(id); + } + + /** + * @param id of exam + * @return exam + */ + public Exam getExamById(int id) { + return examRepository.findById(id).orElseThrow(() -> new NoSuchElementWithProvidedIdException(id)); + } + + /** + * @param groups set od groups (max 4) + * @return set of exams for specific groups + */ + public Set getExamByGroup(Set groups) { + if (groups.size() > 4 || groups.isEmpty()) + throw new UnsupportedCountOfArgumentsException(1, 5, groups.size()); + List groupList = new ArrayList<>(groups); + return switch (groupList.size()) { + case 4 -> examRepository.findExamsByGroupsIdentifier( + groupList.get(0), groupList.get(1), groupList.get(2), groupList.get(3)); + case 3 -> examRepository.findExamsByGroupsIdentifier( + groupList.get(0), groupList.get(1), groupList.get(2)); + case 2 -> examRepository.findExamsByGroupsIdentifier( + groupList.get(0), groupList.get(1)); + case 1 -> examRepository.findExamsByGroupsIdentifier( + groupList.get(0)); + default -> Set.of(); + }; + } + + /** + * @return list of examTypes + */ + public List getExamTypes() { + return examTypeRepository.findAll(); + } +} diff --git a/src/main/java/org/pkwmtt/examCalendar/dto/ExamDto.java b/src/main/java/org/pkwmtt/examCalendar/dto/ExamDto.java new file mode 100644 index 0000000..f1cfdfb --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/dto/ExamDto.java @@ -0,0 +1,33 @@ +package org.pkwmtt.examCalendar.dto; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@RequiredArgsConstructor +public class ExamDto { + + @NotBlank + @Size(max = 255, message = "max size of field is 255") + private final String title; + + @Size(max = 255, message = "max size of field is 255") + private final String description; + + @Future(message = "Date must be in the future") + @NotNull + private final LocalDateTime date; + + @NotBlank + @Size(max = 255, message = "max size of field is 255") + private final String examGroups; + + @NotNull + private final String examType; +} diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/Exam.java b/src/main/java/org/pkwmtt/examCalendar/entity/Exam.java new file mode 100644 index 0000000..89fe16d --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/Exam.java @@ -0,0 +1,48 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.pkwmtt.exceptions.InvalidGroupIdentifierException; + +import java.time.LocalDateTime; +import java.util.Arrays; + +@Entity +@Getter +@Builder(builderClassName = "Builder", buildMethodName = "build") +@RequiredArgsConstructor +@Table(name = "exams") +@AllArgsConstructor +public class Exam { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer examId; + + private String title; + + private String description; + + private LocalDateTime date; + + @Column(name = "`groups`") + private String examGroups; + + @ManyToOne + @JoinColumn(name = "exam_type_id") + private ExamType examType; + + @SuppressWarnings("unused") + public static class Builder { + public Exam build() { + // max length of group identifier is 6 + Arrays.stream(examGroups.split(", ")).forEach(group -> { + if(group.length() > 6) + throw new InvalidGroupIdentifierException(group); + }); + return new Exam(examId, title, description, date, examGroups, examType); + } + } +} diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/ExamType.java b/src/main/java/org/pkwmtt/examCalendar/entity/ExamType.java new file mode 100644 index 0000000..6c5355f --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/ExamType.java @@ -0,0 +1,21 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@RequiredArgsConstructor +@Table(name = "exam_type") +public class ExamType { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer exam_type_id; + + private String name; +} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/GeneralGroup.java b/src/main/java/org/pkwmtt/examCalendar/entity/GeneralGroup.java new file mode 100644 index 0000000..3eb27af --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/GeneralGroup.java @@ -0,0 +1,26 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "`general_group`") +public class GeneralGroup { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer general_group_id; + + private String name; + + @OneToMany(mappedBy = "general_group") + private Set groups; +} diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/Group.java b/src/main/java/org/pkwmtt/examCalendar/entity/Group.java new file mode 100644 index 0000000..050a5cc --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/Group.java @@ -0,0 +1,27 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "`groups`") +public class Group { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer group_id; + + private String name; + + private int group_count; + + @ManyToOne + @JoinColumn(name = "general_group_id") + private GeneralGroup general_group; +} diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/OTPCode.java b/src/main/java/org/pkwmtt/examCalendar/entity/OTPCode.java new file mode 100644 index 0000000..47d3a9a --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/OTPCode.java @@ -0,0 +1,31 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "otp_codes") +public class OTPCode { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer otp_code_id; + + private String code; + + private LocalDateTime timestamp; + + private boolean used; + + @OneToOne + @JoinColumn(name = "user_id", unique = true) + private User user; +} diff --git a/src/main/java/org/pkwmtt/examCalendar/entity/User.java b/src/main/java/org/pkwmtt/examCalendar/entity/User.java new file mode 100644 index 0000000..cb90f87 --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/entity/User.java @@ -0,0 +1,33 @@ +package org.pkwmtt.examCalendar.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.pkwmtt.enums.Role; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "`users`") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer user_id; + + @ManyToOne + @JoinColumn(name = "general_group_id") + private GeneralGroup general_group; + + private String email; + + private boolean is_active; + + private Role role; + + @OneToOne(mappedBy = "user") + private OTPCode otp_code; +} diff --git a/src/main/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapper.java b/src/main/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapper.java new file mode 100644 index 0000000..343f70e --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapper.java @@ -0,0 +1,49 @@ +package org.pkwmtt.examCalendar.mapper; + +import lombok.RequiredArgsConstructor; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.repository.ExamTypeRepository; +import org.pkwmtt.exceptions.ExamTypeNotExistsException; +import org.springframework.stereotype.Component; + +/** + * maps ExamDto to Exam entity. Couldn't be utility class, because needs ExamTypeRepository to validate exam types + */ +@Component +@RequiredArgsConstructor +public class ExamDtoToExamMapper { + private final ExamTypeRepository examTypeRepository; + + /** + * @param examDto examDto object received from request + * @return Exam entity WITHOUT examId which should be assigned by database + * Also contains examType field converted from String do ExamType + */ + public Exam mapToNewExam(ExamDto examDto) { + return Exam.builder() + .title(examDto.getTitle()) + .description(examDto.getDescription()) + .date(examDto.getDate()) + .examGroups(examDto.getExamGroups()) + .examType(examTypeRepository.findByName(examDto.getExamType()).orElseThrow(() -> new ExamTypeNotExistsException(examDto.getExamType()))) + .build(); + } + + /** + * @param examDto examDto object received from request + * @param id of Exam that need to be modified + * @return Exam entity WITH examId that allow to update entity in database instead of creating new one + * Also contains examType field converted from String do ExamType + */ + public Exam mapToExistingExam(ExamDto examDto, int id) { + return Exam.builder() + .examId(id) + .title(examDto.getTitle()) + .description(examDto.getDescription()) + .date(examDto.getDate()) + .examGroups(examDto.getExamGroups()) + .examType(examTypeRepository.findByName(examDto.getExamType()).orElseThrow(() -> new ExamTypeNotExistsException(examDto.getExamType()))) + .build(); + } +} diff --git a/src/main/java/org/pkwmtt/examCalendar/repository/ExamRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/ExamRepository.java new file mode 100644 index 0000000..e6d3454 --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/repository/ExamRepository.java @@ -0,0 +1,74 @@ +package org.pkwmtt.examCalendar.repository; + +import org.pkwmtt.examCalendar.entity.Exam; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Set; + +public interface ExamRepository extends JpaRepository { + + /** + * fetch all data using one query + * @param group1 group identifier + * @param group2 group identifier + * @param group3 group identifier + * @param group4 group identifier + * @return set of Exams for specific groups + */ + @Query("SELECT e FROM Exam e JOIN FETCH e.examType WHERE " + + "e.examGroups LIKE CONCAT('%', :g1, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g2, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g3, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g4, '%') ") + Set findExamsByGroupsIdentifier( + @Param("g1") String group1, + @Param("g2") String group2, + @Param("g3") String group3, + @Param("g4") String group4 + ); + + /** + * fetch all data using one query + * @param group1 group identifier + * @param group2 group identifier + * @param group3 group identifier + * @return set of Exams for specific groups + */ + @Query("SELECT e FROM Exam e JOIN FETCH e.examType WHERE " + + "e.examGroups LIKE CONCAT('%', :g1, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g2, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g3, '%') ") + Set findExamsByGroupsIdentifier( + @Param("g1") String group1, + @Param("g2") String group2, + @Param("g3") String group3 + ); + + /** + * fetch all data using one query + * @param group1 group identifier + * @param group2 group identifier + * @return set of Exams for specific groups + */ + @Query("SELECT e FROM Exam e JOIN FETCH e.examType WHERE " + + "e.examGroups LIKE CONCAT('%', :g1, '%') OR " + + "e.examGroups LIKE CONCAT('%', :g2, '%')" ) + Set findExamsByGroupsIdentifier( + @Param("g1") String group1, + @Param("g2") String group2 + ); + + /** + * fetch all data using one query + * @param group group identifier + * @return set of Exams for specific group + */ + @Query("SELECT e FROM Exam e JOIN FETCH e.examType WHERE " + + "e.examGroups LIKE CONCAT('%', :gg, '%')") + Set findExamsByGroupsIdentifier( + @Param("gg") String group + ); + +} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/examCalendar/repository/ExamTypeRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/ExamTypeRepository.java new file mode 100644 index 0000000..c14d733 --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/repository/ExamTypeRepository.java @@ -0,0 +1,10 @@ +package org.pkwmtt.examCalendar.repository; + +import org.pkwmtt.examCalendar.entity.ExamType; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ExamTypeRepository extends JpaRepository { + Optional findByName(String name); +} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/repository/GeneralGroupRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/GeneralGroupRepository.java similarity index 61% rename from src/main/java/org/pkwmtt/repository/GeneralGroupRepository.java rename to src/main/java/org/pkwmtt/examCalendar/repository/GeneralGroupRepository.java index a4c1c55..62f4fbb 100644 --- a/src/main/java/org/pkwmtt/repository/GeneralGroupRepository.java +++ b/src/main/java/org/pkwmtt/examCalendar/repository/GeneralGroupRepository.java @@ -1,6 +1,6 @@ -package org.pkwmtt.repository; +package org.pkwmtt.examCalendar.repository; -import org.pkwmtt.entity.GeneralGroup; +import org.pkwmtt.examCalendar.entity.GeneralGroup; import org.springframework.data.jpa.repository.JpaRepository; public interface GeneralGroupRepository extends JpaRepository { diff --git a/src/main/java/org/pkwmtt/examCalendar/repository/GroupRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/GroupRepository.java new file mode 100644 index 0000000..98bb7a3 --- /dev/null +++ b/src/main/java/org/pkwmtt/examCalendar/repository/GroupRepository.java @@ -0,0 +1,7 @@ +package org.pkwmtt.examCalendar.repository; + +import org.pkwmtt.examCalendar.entity.Group; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/repository/OTPCodeRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/OTPCodeRepository.java similarity index 60% rename from src/main/java/org/pkwmtt/repository/OTPCodeRepository.java rename to src/main/java/org/pkwmtt/examCalendar/repository/OTPCodeRepository.java index 4f79485..848b4d4 100644 --- a/src/main/java/org/pkwmtt/repository/OTPCodeRepository.java +++ b/src/main/java/org/pkwmtt/examCalendar/repository/OTPCodeRepository.java @@ -1,6 +1,6 @@ -package org.pkwmtt.repository; +package org.pkwmtt.examCalendar.repository; -import org.pkwmtt.entity.OTPCode; +import org.pkwmtt.examCalendar.entity.OTPCode; import org.springframework.data.jpa.repository.JpaRepository; public interface OTPCodeRepository extends JpaRepository { diff --git a/src/main/java/org/pkwmtt/repository/UserRepository.java b/src/main/java/org/pkwmtt/examCalendar/repository/UserRepository.java similarity index 60% rename from src/main/java/org/pkwmtt/repository/UserRepository.java rename to src/main/java/org/pkwmtt/examCalendar/repository/UserRepository.java index 71ccd75..acdf767 100644 --- a/src/main/java/org/pkwmtt/repository/UserRepository.java +++ b/src/main/java/org/pkwmtt/examCalendar/repository/UserRepository.java @@ -1,6 +1,6 @@ -package org.pkwmtt.repository; +package org.pkwmtt.examCalendar.repository; -import org.pkwmtt.entity.User; +import org.pkwmtt.examCalendar.entity.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository { diff --git a/src/main/java/org/pkwmtt/exceptions/ExamTypeNotExistsException.java b/src/main/java/org/pkwmtt/exceptions/ExamTypeNotExistsException.java new file mode 100644 index 0000000..5e8171a --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/ExamTypeNotExistsException.java @@ -0,0 +1,7 @@ +package org.pkwmtt.exceptions; + +public class ExamTypeNotExistsException extends RuntimeException { + public ExamTypeNotExistsException(String examType) { + super("Invalid exam type " + examType); + } +} diff --git a/src/main/java/org/pkwmtt/exceptions/InvalidGroupIdentifierException.java b/src/main/java/org/pkwmtt/exceptions/InvalidGroupIdentifierException.java new file mode 100644 index 0000000..4faadac --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/InvalidGroupIdentifierException.java @@ -0,0 +1,7 @@ +package org.pkwmtt.exceptions; + +public class InvalidGroupIdentifierException extends RuntimeException { + public InvalidGroupIdentifierException(String groupIdentifier) { + super("Invalid group identifier: " + groupIdentifier); + } +} diff --git a/src/main/java/org/pkwmtt/exceptions/NoSuchElementWithProvidedIdException.java b/src/main/java/org/pkwmtt/exceptions/NoSuchElementWithProvidedIdException.java new file mode 100644 index 0000000..e17eead --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/NoSuchElementWithProvidedIdException.java @@ -0,0 +1,7 @@ +package org.pkwmtt.exceptions; + +public class NoSuchElementWithProvidedIdException extends RuntimeException{ + public NoSuchElementWithProvidedIdException(int id) { + super("No such element with id: " + id); + } +} diff --git a/src/main/java/org/pkwmtt/exceptions/UnsupportedCountOfArgumentsException.java b/src/main/java/org/pkwmtt/exceptions/UnsupportedCountOfArgumentsException.java new file mode 100644 index 0000000..709978a --- /dev/null +++ b/src/main/java/org/pkwmtt/exceptions/UnsupportedCountOfArgumentsException.java @@ -0,0 +1,8 @@ +package org.pkwmtt.exceptions; + +public class UnsupportedCountOfArgumentsException extends RuntimeException { + public UnsupportedCountOfArgumentsException(int expectedMin, int expectedMax, int provided) { + super("Invalid count of arguments provided: " + provided + + " expected more than: " + expectedMin + " less than: " + expectedMax); + } +} diff --git a/src/main/java/org/pkwmtt/repository/ExamRepository.java b/src/main/java/org/pkwmtt/repository/ExamRepository.java deleted file mode 100644 index 2faafaa..0000000 --- a/src/main/java/org/pkwmtt/repository/ExamRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.pkwmtt.repository; - -import org.pkwmtt.entity.Exam; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ExamRepository extends JpaRepository { -} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/repository/ExamTypeRepository.java b/src/main/java/org/pkwmtt/repository/ExamTypeRepository.java deleted file mode 100644 index 1b7d38c..0000000 --- a/src/main/java/org/pkwmtt/repository/ExamTypeRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.pkwmtt.repository; - -import org.pkwmtt.entity.ExamType; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ExamTypeRepository extends JpaRepository { -} \ No newline at end of file diff --git a/src/main/java/org/pkwmtt/repository/GroupRepository.java b/src/main/java/org/pkwmtt/repository/GroupRepository.java deleted file mode 100644 index b2396a9..0000000 --- a/src/main/java/org/pkwmtt/repository/GroupRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.pkwmtt.repository; - -import org.pkwmtt.entity.StudentGroup; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface GroupRepository extends JpaRepository { -} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/examCalendar/ExamControllerTest.java b/src/test/java/org/pkwmtt/examCalendar/ExamControllerTest.java new file mode 100644 index 0000000..5f7f6ae --- /dev/null +++ b/src/test/java/org/pkwmtt/examCalendar/ExamControllerTest.java @@ -0,0 +1,620 @@ +package org.pkwmtt.examCalendar; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.pkwmtt.examCalendar.repository.ExamRepository; +import org.pkwmtt.examCalendar.repository.ExamTypeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * integration tests of ExamCalendar + */ +@SpringBootTest +@AutoConfigureMockMvc +class ExamControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ExamTypeRepository examTypeRepository; + + @Autowired + private ExamRepository examRepository; + + @Autowired + private ObjectMapper mapper; + + @BeforeEach + void setupBeforeEach() { + examRepository.deleteAll(); + examTypeRepository.deleteAll(); + } + + // + + /** + * check if addExam endpoint create new exam with correct URI and correct data + */ + @Test + void addExamWithCorrectData() throws Exception { +// given + createExampleExamType("Project"); + ExamDto examDtoRequest = createExampleExamDto("Project"); + String json = mapper.writeValueAsString(examDtoRequest); + + MvcResult result = mockMvc.perform(MockMvcRequestBuilders + .post("/pkwmtt/api/v1/exams") + .contentType("application/json") + .content(json) + ).andDo(print()) + .andExpect(status().isCreated()) + .andExpect(header().string("Location", containsString("/pkwmtt/api/v1/exams/"))) + .andReturn(); + + String location = result.getResponse().getHeader("Location"); + @SuppressWarnings("DataFlowIssue") + int id = Integer.parseInt(location.substring(location.lastIndexOf("/") + 1)); + + Exam examResponse = examRepository.findById(id).orElseThrow(); + + assertEquals(examDtoRequest.getTitle(), examResponse.getTitle()); + assertEquals(examDtoRequest.getDescription(), examResponse.getDescription()); +// compare dates with minutes level precision + assertEquals( + examDtoRequest.getDate().truncatedTo(ChronoUnit.MINUTES), + examResponse.getDate().truncatedTo(ChronoUnit.MINUTES) + ); + assertEquals(examDtoRequest.getExamGroups(), examResponse.getExamGroups()); + assertEquals(examDtoRequest.getExamType(), examResponse.getExamType().getName()); + } + + @Test + void addExamWithBlankExamTitle() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); +// no exam title + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("title : must not be blank", result); + } + + @Test + void addExamWithBlankExamDescription() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); +// no exam description + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isCreated(), requestData); + + String location = result.getResponse().getHeader("Location"); + @SuppressWarnings("DataFlowIssue") + int id = Integer.parseInt(location.substring(location.lastIndexOf("/") + 1)); + + Exam examResponse = examRepository.findById(id).orElseThrow(); + assertNull(examResponse.getDescription()); + } + + @Test + void addExamWithBlankDate() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); +// no date + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("date : must not be null", result); + } + + @Test + void addExamWithBlankExamGroups() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); +// no examGroups + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("examGroups : must not be blank", result); + } + + @Test + void addExamWithNullExamTypes() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); +// no examType + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("examType : must not be null", result); + } + + @Test + void addExamWithNotFutureDate() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().minusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("date : Date must be in the future", result); + } + + @Test + void addExamWithEmptyStringExamTitle() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", ""); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("title : must not be blank", result); + } + + @Test + void addExamWithEmptyStringExamGroups() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", ""); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("examGroups : must not be blank", result); + } + + @Test + void addExamWithTooLongExamTitle() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("title : max size of field is 255", result); + } + + @Test + void addExamWithTooLongDescription() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("description : max size of field is 255", result); + } + + @Test + void addExamWithTooLongExamGroups() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + requestData.put("examType", "Project"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("examGroups : max size of field is 255", result); + } + + @Test + void addExamWithNonExistingExamType() throws Exception { +// given + createExampleExamType("Project"); + Map requestData = new HashMap<>(); + requestData.put("title", "Math exam"); + requestData.put("description", "first exam"); + requestData.put("date", LocalDateTime.now().plusDays(1).toString()); + requestData.put("examGroups", "12K2, L04"); + requestData.put("examType", "NonExistingExamType"); + +// when + MvcResult result = assertPostRequest(status().isBadRequest(), requestData); + +// then + assertResponseMessage("Invalid exam type NonExistingExamType", result); + } + + + // + + // + @Test + void modifyExamWithCorrectData() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + ExamDto examDto = createExampleExamDto(examType.getName()); + +// when + assertPutRequest(status().isNoContent(), examDto, id); + +// then + Exam responseExam = examRepository.findById(id).orElseThrow(); + assertEquals("Math exam", responseExam.getTitle()); + assertEquals("first exam", responseExam.getDescription()); + assertEquals( + LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MINUTES), + responseExam.getDate().truncatedTo(ChronoUnit.MINUTES) + ); + assertEquals("12K2, L04", responseExam.getExamGroups()); + } + + @Test + void modifyExamWithIncorrectExamId() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + ExamDto examDto = createExampleExamDto(examType.getName()); + + int invalidId = Integer.MAX_VALUE - 10; + assertNotEquals(invalidId, id); +// when + MvcResult result = assertPutRequest(status().isNotFound(), examDto, invalidId); + +// then + assertResponseMessage("No such element with id: " + (invalidId), result); + + } +// + + // + @Test + void deleteExamWithCorrectArguments() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + +// when + assertDeleteRequest(status().isNoContent(), id); + +// then + assertTrue(examRepository.findById(id).isEmpty()); + } + + @Test + void deleteNonExistingExam() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + int invalidId = Integer.MAX_VALUE - 10; + assertNotEquals(invalidId, id); + +// when + MvcResult result = assertDeleteRequest(status().isNotFound(), invalidId); + +// then + assertTrue(examRepository.findById(id).isPresent()); + assertResponseMessage("No such element with id: " + (invalidId), result); + } + + // + + // + + @Test + void getExamByIdWithCorrectId() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + +// when + MvcResult result = assertGetByIdRequest(status().isOk(), id); + JsonNode responseNode = mapper.readTree(result.getResponse().getContentAsString()); + +// then + assertEquals(exam.getTitle(), responseNode.get("title").asText()); + assertEquals(exam.getDescription(), responseNode.get("description").asText()); + assertEquals( + exam.getDate().truncatedTo(ChronoUnit.MINUTES), + LocalDateTime.parse(responseNode.get("date").textValue()).truncatedTo(ChronoUnit.MINUTES) + ); + assertEquals(exam.getExamGroups(), responseNode.get("examGroups").asText()); + assertEquals(mapper.readTree(mapper.writeValueAsString(exam.getExamType())), responseNode.get("examType")); + } + + @Test + void getNonExistingExamById() throws Exception { +// given + ExamType examType = createExampleExamType("Exam"); + Exam exam = createExampleExam(examType); + int id = examRepository.save(exam).getExamId(); + int invalidId = Integer.MAX_VALUE - 10; + assertNotEquals(invalidId, id); + +// when + MvcResult result = assertGetByIdRequest(status().isNotFound(), invalidId); + +// then + assertResponseMessage("No such element with id: " + (invalidId), result); + } + +// + + @Test + void getExams() { +// TODO: test getExamsByGroups after implementing new version + } + + // + + @Test + void getExamTypesWhenExamTypesExists() throws Exception { +// given + ExamType exam = createExampleExamType("Exam"); + ExamType project = createExampleExamType("Project"); + +// when + MvcResult result = assertGetExamTypesRequest(status().isOk()); + JsonNode responseArray = mapper.readTree(result.getResponse().getContentAsString()); + +// then + assertEquals(2, responseArray.size()); + assertTrue(responseArray.valueStream().anyMatch(e -> e.get("name").asText().equals(exam.getName()))); + assertTrue(responseArray.valueStream().anyMatch(e -> e.get("name").asText().equals(project.getName()))); + } + + @Test + void getExamTypesWhenExamTypesNotExists() throws Exception { +// given +// when + MvcResult result = mockMvc.perform(MockMvcRequestBuilders + .get("/pkwmtt/api/v1/exams/exam-types") + .contentType("application/json") + ).andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + JsonNode responseArray = mapper.readTree(result.getResponse().getContentAsString()); + +// then + assertEquals(0, responseArray.size()); + } + + // + + // + + /** + * this method create examType object and add it to repository + * @param name of new examType + * @return created examType object + */ + private ExamType createExampleExamType(String name) { + ExamType examType = ExamType.builder().name(name).build(); + examTypeRepository.save(examType); + return examType; + } + + /** + * this method don't add created Exam to repository, because in that case id of created Exam would be unreachable + * @param type ExamType object which is required argument of Exam + * @return created Exam + */ + private Exam createExampleExam(ExamType type) { + return Exam.builder() + .title("Exam") + .description("Exam description") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("11K1, L01") + .examType(type) + .build(); + } + + /** + * @param examTypeName name of type of exam as String + * @return created ExamDto + */ + private ExamDto createExampleExamDto(String examTypeName) { + return new ExamDto( + "Math exam", + "first exam", + LocalDateTime.now().plusDays(1), + "12K2, L04", + examTypeName + ); + } + + /** + * compare error message form response with expected value + * @param expectedMessage full message that is expected in response + * @param result response generated by mockMvc.perform() or one of assert[httpMethod]Request() + * @throws Exception + */ + private void assertResponseMessage(String expectedMessage, MvcResult result) throws Exception { + JsonNode jsonResponse = mapper.readTree(result.getResponse().getContentAsString()); + assertTrue(jsonResponse.has("message")); + assertEquals(expectedMessage, jsonResponse.get("message").asText()); + } + + /** + * method send POST request to ExamController with content as JSON attached to body and then check if response + * code is the same as expected + * @param expectedStatus status().[http response] (example: status().isCreated() ) + * @param content object that would be mapped to JSON by ObjectMapper and then attached to request + * it could be dto object or Map + * @return MvcResult object which could be used to capture response body + * @throws Exception + */ + private MvcResult assertPostRequest(ResultMatcher expectedStatus, Object content) throws Exception { + return mockMvc.perform(MockMvcRequestBuilders + .post("/pkwmtt/api/v1/exams") + .contentType("application/json") + .content(mapper.writeValueAsString(content)) + ).andDo(print()) + .andExpect(expectedStatus) + .andReturn(); + } + + /** + * method send PUT request to ExamController with content as JSON attached to body and examId as pathID. + * Then check if response code is the same as expected + * @param expectedStatus status().[http response] (example: status().isNoContent() ) + * @param content object that would be mapped to JSON by ObjectMapper and then attached to request + * @param pathId id of resource that would be updated + * @return MvcResult object which could be used to capture response body + * @throws Exception + */ + private MvcResult assertPutRequest(ResultMatcher expectedStatus, Object content, int pathId) throws Exception { + return mockMvc.perform(MockMvcRequestBuilders + .put("/pkwmtt/api/v1/exams/{id}", pathId) + .contentType("application/json") + .content(mapper.writeValueAsString(content)) + ).andDo(print()) + .andExpect(expectedStatus) + .andReturn(); + } + + /** + * method send DELETE request to ExamController with examId as pathID. + * Then check if response code is the same as expected + * @param expectedStatus status().[http response] (example: status().isNoContent() ) + * @param pathId id of resource that would be deleted + * @return MvcResult object which could be used to capture response body + * @throws Exception + */ + private MvcResult assertDeleteRequest(ResultMatcher expectedStatus, int pathId) throws Exception { + return mockMvc.perform(MockMvcRequestBuilders + .delete("/pkwmtt/api/v1/exams/{id}", pathId) + .contentType("application/json") + ).andDo(print()) + .andExpect(expectedStatus) + .andReturn(); + } + + /** + * method send GET request to ExamController at /pkwmtt/api/v1/exams/{id} URI with examId as pathID. + * Then check if response code is the same as expected + * @param expectedStatus status().[http response] (example: status().isOk() ) + * @param pathId id of resource that would be returned + * @return MvcResult object which could be used to capture response body + * @throws Exception + */ + private MvcResult assertGetByIdRequest(ResultMatcher expectedStatus, int pathId) throws Exception { + return mockMvc.perform(MockMvcRequestBuilders + .get("/pkwmtt/api/v1/exams/{id}", pathId) + .contentType("application/json") + ).andDo(print()) + .andExpect(expectedStatus) + .andReturn(); + } + + /** + * method send GET request to ExamController at /pkwmtt/api/v1/exams/exam-types URI. + * Then check if response code is the same as expected + * @param expectedStatus expectedStatus status().[http response] (example: status().isOk() ) + * @return MvcResult object which could be used to capture response body + * @throws Exception + */ + private MvcResult assertGetExamTypesRequest(ResultMatcher expectedStatus) throws Exception { + return mockMvc.perform(MockMvcRequestBuilders + .get("/pkwmtt/api/v1/exams/exam-types") + .contentType("application/json") + ).andDo(print()) + .andExpect(expectedStatus) + .andReturn(); + } + +// + +} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/examCalendar/ExamServiceTest.java b/src/test/java/org/pkwmtt/examCalendar/ExamServiceTest.java new file mode 100644 index 0000000..c86b0ab --- /dev/null +++ b/src/test/java/org/pkwmtt/examCalendar/ExamServiceTest.java @@ -0,0 +1,331 @@ +package org.pkwmtt.examCalendar; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.pkwmtt.examCalendar.mapper.ExamDtoToExamMapper; +import org.pkwmtt.examCalendar.repository.ExamRepository; +import org.pkwmtt.exceptions.UnsupportedCountOfArgumentsException; + +import java.lang.reflect.Field; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ExamServiceTest { + + @Mock + private ExamRepository examRepository; + + @Mock + private ExamDtoToExamMapper examDtoToExamMapper; + + @InjectMocks + private ExamService examService; + + @Test + void addExam() { +// given + int examId = 1; + ExamDto examDto = new ExamDto( + "Math exam", + "desc", + LocalDateTime.now().plusDays(1), + "12K2, 13L1", + "Exam" + ); + Exam exam = Exam.builder() + .title("Math exam") + .description("desc") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, 13L1") + .examType(new ExamType(1, "Exam")) + .build(); + when(examDtoToExamMapper.mapToNewExam(examDto)).thenReturn(exam); + +// assign exam id in repository + when(examRepository.save(exam)).thenAnswer(invocation -> { + Exam newExam = invocation.getArgument(0, Exam.class); + Field field = Exam.class.getDeclaredField("examId"); + field.setAccessible(true); + field.set(newExam, examId); + return newExam; + }); +// when + int result = examService.addExam(examDto); +// then + assertEquals(examId, result); + verify(examRepository).save(exam); + verify(examDtoToExamMapper).mapToNewExam(examDto); + } + + /************************************************************************************/ +//modify exam + @Test + void shouldModifyExamWhenIdExists() { + // given + int examId = 1; + ExamDto examDto = mock(ExamDto.class); + Exam exam = mock(Exam.class); + + when(examDtoToExamMapper.mapToExistingExam(examDto, examId)).thenReturn(exam); + when(examRepository.findById(examId)).thenReturn(Optional.of(exam)); +// when + examService.modifyExam(examDto, examId); +// then + verify(examDtoToExamMapper).mapToExistingExam(examDto, examId); + verify(examRepository).save(exam); + } + + @Test + void shouldThrowWhenExamIdNotExists() { + // given + int examId = 5; + ExamDto examDto = mock(ExamDto.class); + when(examRepository.findById(examId)).thenThrow(new NoSuchElementException("Exam not found")); +// when + RuntimeException exception = assertThrows( + NoSuchElementException.class, + () -> examService.modifyExam(examDto, examId) + ); +// then + verify(examDtoToExamMapper, never()).mapToExistingExam(examDto, examId); + verify(examRepository, never()).save(any()); + assertEquals("Exam not found", exception.getMessage()); + } + + /************************************************************************************/ +//delete exam + @Test + void shouldDeleteExamWhenIdExists() { +// given + int examId = 1; + when(examRepository.findById(examId)).thenReturn(Optional.of(mock(Exam.class))); +// when + examService.deleteExam(examId); +// then + verify(examRepository).deleteById(examId); + } + + @Test + void shouldThrowExceptionWhenExamIdNotExists() { +// given + int examId = 5; + when(examRepository.findById(examId)).thenThrow(new NoSuchElementException("Exam not found")); +// when + RuntimeException exception = assertThrows( + NoSuchElementException.class, + () -> examService.deleteExam(examId) + ); +// then + verify(examRepository, never()).deleteById(examId); + assertEquals("Exam not found", exception.getMessage()); + } + + /************************************************************************************/ +// getExamById + @Test + void getExamById() { +// given + int examId = 1; + when(examRepository.findById(examId)).thenReturn(Optional.of(mock(Exam.class))); +// when + Exam exam = examService.getExamById(examId); +// then + verify(examRepository).findById(examId); + assertNotNull(exam); + } + + @Test + void shouldThrowExceptionWhenExamNotFound() { +// given + int examId = 5; + when(examRepository.findById(examId)).thenThrow(new NoSuchElementException("Exam not found")); +// when + RuntimeException exception = assertThrows( + NoSuchElementException.class, + () -> examService.getExamById(examId) + ); +// then + assertEquals("Exam not found", exception.getMessage()); + } + + // getExamByGroup + @Test + void shouldThrowWithMoreThan4Arguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + groups.add("13L1"); + groups.add("13A2"); + groups.add("41S2"); + groups.add("11S3"); +// when + RuntimeException exception = assertThrows( + UnsupportedCountOfArgumentsException.class, + () -> examService.getExamByGroup(groups) + ); +// then + assertEquals( + "Invalid count of arguments provided: 5 expected more than: 1 less than: 5", + exception.getMessage() + ); + } + + + @Test + void shouldCallRepositoryWith4Arguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + groups.add("13L1"); + groups.add("13A2"); + groups.add("41S2"); + Exam mockExam = mock(Exam.class); + Set exams = new HashSet<>(); + exams.add(mockExam); + when(examRepository.findExamsByGroupsIdentifier(any(), any(), any(), any())).thenReturn(exams); +// when + Set result = examService.getExamByGroup(groups); +// then + List> cap = new ArrayList<>(); + for (int i = 0; i < 4; ++i) + cap.add(ArgumentCaptor.forClass(String.class)); + + verify(examRepository).findExamsByGroupsIdentifier( + cap.get(0).capture(), + cap.get(1).capture(), + cap.get(2).capture(), + cap.get(3).capture() + ); + Set passedGroups = cap.stream().map(ArgumentCaptor::getValue).collect(Collectors.toSet()); + + assertEquals(groups, passedGroups); + assertEquals(exams, result); + } + + + @Test + void shouldCallRepositoryWith3Arguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + groups.add("13L1"); + groups.add("13A2"); + Exam mockExam = mock(Exam.class); + Set exams = new HashSet<>(); + exams.add(mockExam); + when(examRepository.findExamsByGroupsIdentifier(any(), any(), any())).thenReturn(exams); +// when + Set result = examService.getExamByGroup(groups); +// then + List> cap = new ArrayList<>(); + for (int i = 0; i < 3; ++i) + cap.add(ArgumentCaptor.forClass(String.class)); + + verify(examRepository).findExamsByGroupsIdentifier( + cap.get(0).capture(), + cap.get(1).capture(), + cap.get(2).capture() + ); + Set passedGroups = cap.stream().map(ArgumentCaptor::getValue).collect(Collectors.toSet()); + + assertEquals(groups, passedGroups); + assertEquals(exams, result); + } + + @Test + void shouldCallRepositoryWith2Arguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + groups.add("13L1"); + Exam mockExam = mock(Exam.class); + Set exams = new HashSet<>(); + exams.add(mockExam); + when(examRepository.findExamsByGroupsIdentifier(any(), any())).thenReturn(exams); +// when + Set result = examService.getExamByGroup(groups); +// then + List> cap = new ArrayList<>(); + for (int i = 0; i < 2; ++i) + cap.add(ArgumentCaptor.forClass(String.class)); + + verify(examRepository).findExamsByGroupsIdentifier( + cap.get(0).capture(), + cap.get(1).capture() + ); + Set passedGroups = cap.stream().map(ArgumentCaptor::getValue).collect(Collectors.toSet()); + + assertEquals(groups, passedGroups); + assertEquals(exams, result); + } + + @Test + void shouldCallRepositoryWithSingleArguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + Exam mockExam = mock(Exam.class); + Set exams = new HashSet<>(); + exams.add(mockExam); + when(examRepository.findExamsByGroupsIdentifier(any())).thenReturn(exams); +// when + Set result = examService.getExamByGroup(groups); +// then + ArgumentCaptor cap = ArgumentCaptor.forClass(String.class); + + verify(examRepository).findExamsByGroupsIdentifier(cap.capture()); + Set passedGroups = new HashSet<>(); + passedGroups.add(cap.getValue()); + + assertEquals(groups, passedGroups); + assertEquals(exams, result); + } + + + @Test + void shouldCallRepositoryWithDuplicatesOf4UniqueArguments() { +// given + Set groups = new HashSet<>(); + groups.add("12K2"); + groups.add("13L1"); + groups.add("13A2"); + groups.add("41S2"); + groups.add("41S2"); + groups.add("13L1"); + Exam mockExam = mock(Exam.class); + Set exams = new HashSet<>(); + exams.add(mockExam); + when(examRepository.findExamsByGroupsIdentifier(any(), any(), any(), any())).thenReturn(exams); +// when + Set result = examService.getExamByGroup(groups); +// then + List> cap = new ArrayList<>(); + for (int i = 0; i < 4; ++i) + cap.add(ArgumentCaptor.forClass(String.class)); + + verify(examRepository).findExamsByGroupsIdentifier( + cap.get(0).capture(), + cap.get(1).capture(), + cap.get(2).capture(), + cap.get(3).capture() + ); + Set passedGroups = cap.stream().map(ArgumentCaptor::getValue).collect(Collectors.toSet()); + + assertEquals(groups, passedGroups); + assertEquals(exams, result); + assertEquals(4, passedGroups.size()); + } + +} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/examCalendar/dto/ExamDtoTest.java b/src/test/java/org/pkwmtt/examCalendar/dto/ExamDtoTest.java new file mode 100644 index 0000000..b14edc6 --- /dev/null +++ b/src/test/java/org/pkwmtt/examCalendar/dto/ExamDtoTest.java @@ -0,0 +1,150 @@ +package org.pkwmtt.examCalendar.dto; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.time.LocalDateTime; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ExamDtoTest { + + private final Validator validator; + + public ExamDtoTest() { + this.validator = Validation.buildDefaultValidatorFactory().getValidator(); + } + + @Mock + private ExamDto examDto; + + @Test + void validData() { +// given + ExamDto examDto = new ExamDto( + "Math exam", + "First exam", + LocalDateTime.now().plusDays(1), + "12K2, K04", + "exam" + ); +// when, then + assertTrue(validator.validate(examDto).isEmpty()); + } + + + // empty Strings + @Test + void emptyStringTitle() { + // given + ExamDto examDto = new ExamDto( + "", + "First exam", + LocalDateTime.now().plusDays(1), + "12K2, K04", + "exam" + ); +// when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("title"))); + } + + @Test + void emptyExamGroups() { + // given + ExamDto examDto = new ExamDto( + "Math exam", + "First exam", + LocalDateTime.now().plusDays(1), + "", + "exam" + ); +// when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("examGroups"))); + } + +// to long Strings + + @Test + void toLongStringTitle() { + // given + ExamDto examDto = new ExamDto( +// 256 characters + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "First exam", + LocalDateTime.now().plusDays(1), + "12K2, K04", + "exam" + ); +// when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("title"))); + } + + @Test + void toLongDescription() { + // given + ExamDto examDto = new ExamDto( + "Math exam", +// 256 characters + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + LocalDateTime.now().plusDays(1), + "12K2, K04", + "exam" + ); +// when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("description"))); + } + + @Test + void toLongExamGroups() { + // given + ExamDto examDto = new ExamDto( + "Math exam", + "First exam", + LocalDateTime.now().plusDays(1), + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "exam" + ); +// when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("examGroups"))); + } + +// date not in future + + @Test + void dateNotInFuture() { + // given + ExamDto examDto = new ExamDto( + "Math exam", + "First exam", + LocalDateTime.now().minusHours(1), + "12K2, K04", + "exam" + ); + // when + Set> violations = validator.validate(examDto); +// then + assertFalse(validator.validate(examDto).isEmpty()); + assertTrue(violations.stream().anyMatch(v -> v.getPropertyPath().toString().equals("date"))); + } + +} \ No newline at end of file diff --git a/src/test/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapperTest.java b/src/test/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapperTest.java new file mode 100644 index 0000000..45081ae --- /dev/null +++ b/src/test/java/org/pkwmtt/examCalendar/mapper/ExamDtoToExamMapperTest.java @@ -0,0 +1,150 @@ +package org.pkwmtt.examCalendar.mapper; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.pkwmtt.examCalendar.dto.ExamDto; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.pkwmtt.examCalendar.repository.ExamTypeRepository; +import org.pkwmtt.exceptions.InvalidGroupIdentifierException; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class ExamDtoToExamMapperTest { + + @Mock + private ExamTypeRepository examTypeRepository; + + @InjectMocks + private ExamDtoToExamMapper examDtoToExamMapper; + + private ExamDto examDto; + private String examTypeName; + +// @BeforeEach +// void setup() { +// +// } + + /**********************************************************************************/ +// mapToNewExam + @Test + void isFieldsMappedProperlyToNewExam() { +// given + String examTypeName = "exam"; + ExamDto examDto = new ExamDto( + "Math exam", + "Linear algebra", + LocalDateTime.now().plusDays(1), + "12K2, 13S1", + examTypeName + ); + when(examTypeRepository.findByName(examTypeName)).thenReturn( + Optional.of(ExamType.builder() + .name(examTypeName) + .build()) + ); +// when + Exam exam = examDtoToExamMapper.mapToNewExam(examDto); +// then +// test fields + assertEquals(examDto.getTitle(), exam.getTitle()); + assertEquals(examDto.getDescription(), exam.getDescription()); + assertEquals(examDto.getDate(), exam.getDate()); + assertEquals(examDto.getExamGroups(), exam.getExamGroups()); + assertEquals(examTypeName, exam.getExamType().getName()); +// test null id + assertNull(exam.getExamId()); + } + + @Test + void ShouldThrowExceptionWhenGroupIdentifierIsLongerThanSixCharactersForNewExam() { + // given + String examTypeName = "exam"; + ExamDto examDto = new ExamDto( + "Math exam", + "Linear algebra", + LocalDateTime.now().plusDays(1), + "12K2, 13S1, Not_Valid_Identifier, 41K1", + examTypeName + ); + when(examTypeRepository.findByName(examTypeName)).thenReturn( + Optional.of(ExamType.builder() + .name(examTypeName) + .build()) + ); +// then + RuntimeException exception = assertThrows( + InvalidGroupIdentifierException.class, + () -> examDtoToExamMapper.mapToNewExam(examDto) + ); + assertEquals("Invalid group identifier: Not_Valid_Identifier", exception.getMessage()); + } + + + /**********************************************************************************/ +// mapToExistingExam + @Test + void isFieldsMappedProperlyToExistingExam() { + // given + int examId = 1; + examTypeName = "exam"; + examDto = new ExamDto( + "Math exam", + "Linear algebra", + LocalDateTime.now().plusDays(1), + "12K2, 13S1", + examTypeName + ); + when(examTypeRepository.findByName(examTypeName)).thenReturn( + Optional.of(ExamType.builder() + .name(examTypeName) + .build()) + ); +// when + Exam exam = examDtoToExamMapper.mapToExistingExam(examDto, examId); +// then +// test fields + assertEquals(examId, exam.getExamId()); + assertEquals(examDto.getTitle(), exam.getTitle()); + assertEquals(examDto.getDescription(), exam.getDescription()); + assertEquals(examDto.getDate(), exam.getDate()); + assertEquals(examDto.getExamGroups(), exam.getExamGroups()); + assertEquals(examTypeName, exam.getExamType().getName()); +// test not null id + assertNotNull(exam.getExamId()); + } + + @Test + void ShouldThrowExceptionWhenGroupIdentifierIsLongerThanSixCharactersForExistingExam() { + // given + int examId = 1; + String examTypeName = "exam"; + ExamDto examDto = new ExamDto( + "Math exam", + "Linear algebra", + LocalDateTime.now().plusDays(1), + "12K2, 13S1, Not_Valid_Identifier, 41K1", + examTypeName + ); + when(examTypeRepository.findByName(examTypeName)).thenReturn( + Optional.of(ExamType.builder() + .name(examTypeName) + .build()) + ); +// then + RuntimeException exception = assertThrows( + InvalidGroupIdentifierException.class, + () -> examDtoToExamMapper.mapToExistingExam(examDto, examId) + ); + assertEquals("Invalid group identifier: Not_Valid_Identifier", exception.getMessage()); + } +} diff --git a/src/test/java/org/pkwmtt/examCalendar/repository/ExamRepositoryTest.java b/src/test/java/org/pkwmtt/examCalendar/repository/ExamRepositoryTest.java new file mode 100644 index 0000000..90f1614 --- /dev/null +++ b/src/test/java/org/pkwmtt/examCalendar/repository/ExamRepositoryTest.java @@ -0,0 +1,182 @@ +package org.pkwmtt.examCalendar.repository; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.pkwmtt.examCalendar.entity.Exam; +import org.pkwmtt.examCalendar.entity.ExamType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Slf4j +@DataJpaTest +class ExamRepositoryTest { + + @Autowired + private ExamRepository examRepository; + + @Autowired + private ExamTypeRepository examTypeRepository; + + private ExamType examType; + + @BeforeEach + void setup(){ + examType = ExamType.builder() + .name("exam") + .build(); + examTypeRepository.save(examType); + } + + /** + * test if method find specific count of exams when 1 or 0 group identifiers match + */ + @Test + void testSingleIdentifierMatch() { +// given + Exam exam1 = Exam.builder() + .title("Exam 1") + .description("Exam 1") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, K03") + .examType(examType) + .build(); + examRepository.save(exam1); + Exam exam2 = Exam.builder() + .title("Exam 2") + .description("Exam 2") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K3, K03, S02") + .examType(examType) + .build(); + examRepository.save(exam2); + Exam exam3 = Exam.builder() + .title("Exam 3") + .description("Exam 3") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("13K1, K05, L05") + .examType(examType) + .build(); + examRepository.save(exam3); + Exam exam4 = Exam.builder() + .title("Exam 4") + .description("Exam 4") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("41K1, L04, P03, I01") + .examType(examType) + .build(); + examRepository.save(exam4); + Exam exam5 = Exam.builder() + .title("Exam 5") + .description("Exam 5") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("11A1, G03, H01, P02") + .examType(examType) + .build(); + examRepository.save(exam5); + + String generalGroup = "12K2"; + String kGroup = "K05"; + String lGroup = "L04"; + String pGroup = "P02"; + +// when + Set exams = examRepository.findExamsByGroupsIdentifier(generalGroup, kGroup, lGroup, pGroup); + List examsTitles = exams.stream().map(Exam::getTitle).toList(); +// then + assertEquals(4, exams.size()); + assertTrue(examsTitles.contains("Exam 1")); + assertTrue(examsTitles.contains("Exam 3")); + assertTrue(examsTitles.contains("Exam 4")); + assertTrue(examsTitles.contains("Exam 5")); + } + + /** + * test if method don't duplicate exams when more than 1 identifier match + */ + @Test + void testMultipleIdentifierMatch() { +// given + Exam exam1 = Exam.builder() + .title("Exam 1") + .description("Exam 1") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, K01, L04, P03, I01") + .examType(examType) + .build(); + examRepository.save(exam1); + Exam exam2 = Exam.builder() + .title("Exam 2") + .description("Exam 2") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, K05, L04, P02") + .examType(examType) + .build(); + examRepository.save(exam2); + Exam exam3 = Exam.builder() + .title("Exam 3") + .description("Exam 3") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, K05, L04, P02, I05") + .examType(examType) + .build(); + examRepository.save(exam3); + + String generalGroup = "12K2"; + String kGroup = "K05"; + String lGroup = "L04"; + String pGroup = "P02"; + +// when + Set exams = examRepository.findExamsByGroupsIdentifier(generalGroup, kGroup, lGroup, pGroup); + List examsTitles = exams.stream().map(Exam::getTitle).toList(); + +// then + assertEquals(3, exams.size()); + assertTrue(examsTitles.contains("Exam 1")); + assertTrue(examsTitles.contains("Exam 2")); + assertTrue(examsTitles.contains("Exam 3")); + } + + /** + * test if method return empty set identifiers don't match + */ + @Test + void testNothingMatch() { +// given + Exam exam1 = Exam.builder() + .title("Exam 1") + .description("Exam 1") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K2, K01,") + .examType(examType) + .build(); + examRepository.save(exam1); + Exam exam2 = Exam.builder() + .title("Exam 2") + .description("Exam 2") + .date(LocalDateTime.now().plusDays(1)) + .examGroups("12K3, L05") + .examType(examType) + .build(); + examRepository.save(exam2); + + String generalGroup = "14K3"; + String kGroup = "K05"; + String lGroup = "L02"; + String pGroup = "P02"; + +// when + Set exams = examRepository.findExamsByGroupsIdentifier(generalGroup, kGroup, lGroup, pGroup); + +// then + assertTrue(exams.isEmpty()); + } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..bb5b179 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,13 @@ +#casue issue by some reason +#spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false +#spring.datasource.username=sa +#spring.datasource.password=sa + +spring.jpa.show-sql=true +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.datasource.driver-class-name=org.h2.Driver +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false +spring.jpa.hibernate.ddl-auto=none +spring.datasource.hikari.initialization-fail-timeout=0 + + diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql new file mode 100644 index 0000000..760aaf7 --- /dev/null +++ b/src/test/resources/schema.sql @@ -0,0 +1,60 @@ +DROP TABLE IF EXISTS exams; +DROP TABLE IF EXISTS exam_type; +DROP TABLE IF EXISTS general_group; +DROP TABLE IF EXISTS groups; +DROP TABLE IF EXISTS otp_codes; +DROP TABLE IF EXISTS users; + +CREATE TABLE exam_type +( + exam_type_id INT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255) +); + +CREATE TABLE general_group +( + general_group_id INT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255) +); + +CREATE TABLE exams +( + exam_id INT PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(255), + description VARCHAR(255), + date TIMESTAMP(6), + "groups" VARCHAR(255), + exam_type_id INT NOT NULL, + FOREIGN KEY (exam_type_id) REFERENCES exam_type (exam_type_id) +); + +CREATE TABLE groups +( + group_id INT PRIMARY KEY AUTO_INCREMENT, + letter CHAR(1) NOT NULL, + group_count INT NOT NULL, + general_group_id INT NOT NULL, + name VARCHAR(255), + FOREIGN KEY (general_group_id) REFERENCES general_group (general_group_id) +); + +CREATE TABLE users +( + user_id INT PRIMARY KEY AUTO_INCREMENT, + general_group_id INT NOT NULL, + email VARCHAR(254) NOT NULL, + is_active BOOLEAN NOT NULL, + role VARCHAR(20) NOT NULL, -- enum zamieniony na VARCHAR + FOREIGN KEY (general_group_id) REFERENCES general_group (general_group_id) +); + +CREATE TABLE otp_codes +( + otp_code_id INT PRIMARY KEY AUTO_INCREMENT, + code VARCHAR(255), + expire TIMESTAMP NOT NULL, + used BOOLEAN NOT NULL, + user_id INT NOT NULL, + timestamp TIMESTAMP(6), + FOREIGN KEY (user_id) REFERENCES users (user_id) +); \ No newline at end of file