Move to java 21 and refactor accordingly

This commit is contained in:
Dmitry Isaenko 2025-05-02 18:25:32 +03:00
parent 97fd90fb5a
commit 79a5a04488
22 changed files with 74 additions and 96 deletions

View file

@ -15,7 +15,7 @@
<name>marinesco</name> <name>marinesco</name>
<description>Home library</description> <description>Home library</description>
<properties> <properties>
<java.version>17</java.version> <java.version>21</java.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@ -84,6 +84,7 @@
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<version>42.7.2</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -18,9 +18,9 @@ import ru.redrise.marinesco.settings.ApplicationSettings;
@Slf4j @Slf4j
@Configuration @Configuration
public class ShinyApplicationRunner { public class ShinyApplicationRunner {
private UserRepository users; private final UserRepository users;
private RolesRepository roles; private final RolesRepository roles;
private ApplicationSettings settings; private final ApplicationSettings settings;
public ShinyApplicationRunner(UserRepository users, RolesRepository roles, ApplicationSettings settings) { public ShinyApplicationRunner(UserRepository users, RolesRepository roles, ApplicationSettings settings) {
this.users = users; this.users = users;
@ -57,7 +57,7 @@ public class ShinyApplicationRunner {
List<UserRole> adminRoleOnlyAthority = roles.findByType(UserRole.Type.ADMIN); List<UserRole> adminRoleOnlyAthority = roles.findByType(UserRole.Type.ADMIN);
if (login == null || login.size() == 0 || password == null || password.size() == 0) { if (login == null || login.isEmpty() || password == null || password.isEmpty()) {
log.warn("No administrator credentials provided, using defaults:\n * Login: root\n * Password: root\n Expected: --admin_login LOGIN --admin_password PASSWORD "); // TODO: Move into properties i18n log.warn("No administrator credentials provided, using defaults:\n * Login: root\n * Password: root\n Expected: --admin_login LOGIN --admin_password PASSWORD "); // TODO: Move into properties i18n
var adminUser = new User("root", encoder.encode("root"), "SuperAdmin", adminRoleOnlyAthority); var adminUser = new User("root", encoder.encode("root"), "SuperAdmin", adminRoleOnlyAthority);
users.save(adminUser); users.save(adminUser);

View file

@ -74,8 +74,8 @@ public class User implements UserDetails{
} }
public boolean isAdmin(){ public boolean isAdmin(){
for (UserRole athority : authorities){ for (UserRole authority : authorities){
if (athority.getAuthority().equals("ROLE_ADMIN")) if (authority.getAuthority().equals("ROLE_ADMIN"))
return true; return true;
} }
return false; return false;

View file

@ -17,17 +17,17 @@ public class UserGenerified {
private String name; private String name;
private String displayName; private String displayName;
private List<UserRole> athorities; private List<UserRole> authorities;
private List<UserRole> athoritiesLost; private List<UserRole> authoritiesLost;
private String password; private String password;
public UserGenerified(User user, List<UserRole> allRolesList){ public UserGenerified(User user, List<UserRole> allRolesList){
this.id = user.getId(); this.id = user.getId();
this.name = user.getUsername(); this.name = user.getUsername();
this.displayName = user.getDisplayname(); this.displayName = user.getDisplayname();
this.athorities = user.getAuthorities(); this.authorities = user.getAuthorities();
this.athoritiesLost = allRolesList.stream() this.authoritiesLost = allRolesList.stream()
.filter(element -> !athorities.contains(element)) .filter(element -> !authorities.contains(element))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
} }

View file

@ -2,6 +2,7 @@ package ru.redrise.marinesco;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data; import lombok.Data;
@Data @Data
@ -10,14 +11,11 @@ public class UserSettingsForm {
@NotNull @NotNull
@NotEmpty(message = "Display name could not be blank") @NotEmpty(message = "Display name could not be blank")
public String displayname; public String displayname;
@Pattern(regexp = "^[a-zA-Z0-9!#+\"\\-<>%^&*$@]{8,32}$|^$",
message = "Password must be at least 8 characters long. Should not exceed 32 characters")
public String newPassword; public String newPassword;
public boolean isNewPasswordSet(){ public boolean isNewPasswordSet(){
return ! newPassword.isBlank(); return ! newPassword.isBlank();
} }
public boolean isNewPasswordValid(){
final int newPasswordLength = newPassword.length();
return newPasswordLength > 8 && newPasswordLength < 32;
}
} }

View file

@ -14,7 +14,7 @@ import jakarta.validation.Payload;
@Target({ElementType.METHOD, ElementType.FIELD}) @Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface LoginOccupiedConstraint { public @interface LoginOccupiedConstraint {
String message() default "Login already taken. Please use anohter one."; String message() default "Login already taken. Please use another one";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {}; Class<? extends Payload>[] payload() default {};
} }

View file

@ -12,9 +12,6 @@ public class LoginOccupiedValidator implements ConstraintValidator<LoginOccupied
@Autowired @Autowired
private UserRepository userRepo; private UserRepository userRepo;
@Override
public void initialize(LoginOccupiedConstraint constraintAnnotation) {}
@Override @Override
public boolean isValid(String login, ConstraintValidatorContext context) { public boolean isValid(String login, ConstraintValidatorContext context) {
return userRepo.findByUsername(login) == null; return userRepo.findByUsername(login) == null;

View file

@ -3,12 +3,7 @@ package ru.redrise.marinesco.library;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@ -29,13 +24,13 @@ import ru.redrise.marinesco.settings.ApplicationSettings;
public class InpxScanner { public class InpxScanner {
private static volatile String lastRunErrors = ""; private static volatile String lastRunErrors = "";
private ThreadPoolTaskExecutor executor; private final ThreadPoolTaskExecutor executor;
private LibraryMetadataRepository libraryMetadataRepository; private final AuthorRepository authorRepository;
private AuthorRepository authorRepository; private final GenreRepository genreRepository;
private GenreRepository genreRepository; private final BookRepository bookRepository;
private BookRepository bookRepository; private final LibraryMetadataRepository libraryMetadataRepository;
private String filesLocation; private final String filesLocation;
public InpxScanner(ThreadPoolTaskExecutor executor, public InpxScanner(ThreadPoolTaskExecutor executor,
ApplicationSettings applicationSettings, ApplicationSettings applicationSettings,
@ -87,7 +82,7 @@ public class InpxScanner {
private File getInpxFile() throws Exception { private File getInpxFile() throws Exception {
final FileSystemResource libraryLocation = new FileSystemResource(filesLocation); final FileSystemResource libraryLocation = new FileSystemResource(filesLocation);
return Stream.of(libraryLocation.getFile().listFiles()) return Arrays.stream(libraryLocation.getFile().listFiles())
.filter(file -> file.getName().endsWith(".inpx")) .filter(file -> file.getName().endsWith(".inpx"))
.findFirst() .findFirst()
.get(); .get();

View file

@ -86,7 +86,7 @@ public class RepackZip {
} }
} catch (Exception e) { } catch (Exception e) {
log.error("{}", e); log.error(e.toString());
} }
log.info("Complete: {}", container.getName()); log.info("Complete: {}", container.getName());
} }

View file

@ -47,17 +47,14 @@ public class GenreSettingsController {
genreRepository.findAll() genreRepository.findAll()
.iterator() .iterator()
.forEachRemaining(element -> genres.add(element)); .forEachRemaining(genres::add);
return new GenresHolder(genres); return new GenresHolder(genres);
} }
@PostMapping @PostMapping
public String getGenresUpdated(@ModelAttribute GenresHolder genreHolder) { public String getGenresUpdated(@ModelAttribute GenresHolder genreHolder) {
genreHolder.getGenres().forEach(genreRepository::save);
for (Genre genre : genreHolder.getGenres())
genreRepository.save(genre);
return "genres_settings"; return "genres_settings";
} }

View file

@ -14,16 +14,15 @@ import ru.redrise.marinesco.library.Genre;
@Slf4j @Slf4j
public class GenresUpload { public class GenresUpload {
public static String upload(Resource resouce, long fileSize, GenreRepository repository) { public static String upload(Resource resource, long fileSize, GenreRepository repository) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resouce.getInputStream()))){ try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))){
if (fileSize == 0) if (fileSize == 0)
throw new Exception("empty file"); throw new Exception("empty file");
String line; String line;
List<Genre> genres = new ArrayList<>(); List<Genre> genres = new ArrayList<>();
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
String arr[] = line.split(":::"); String[] arr = line.split(":::");
if (arr.length != 2) if (arr.length != 2)
throw new Exception("Malformed file"); throw new Exception("Malformed file");
@ -33,10 +32,10 @@ public class GenresUpload {
repository.saveAll(genres); repository.saveAll(genres);
} catch (Exception e) { } catch (Exception e) {
log.debug("{}", e); log.debug(e.toString());
return "Upload failed: " + e.getMessage(); return "Upload failed: " + e.getMessage();
} }
return "Successfully uploaded: " + resouce.getFilename(); return "Successfully uploaded: " + resource.getFilename();
} }
} }

View file

@ -1,6 +1,7 @@
package ru.redrise.marinesco.library.web; package ru.redrise.marinesco.library.web;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -16,7 +17,7 @@ import ru.redrise.marinesco.library.Book;
@Controller @Controller
@RequestMapping("/author") @RequestMapping("/author")
public class AuthorController { public class AuthorController {
private AuthorRepository authorRepository; private final AuthorRepository authorRepository;
public AuthorController(AuthorRepository authorRepository){ public AuthorController(AuthorRepository authorRepository){
this.authorRepository = authorRepository; this.authorRepository = authorRepository;
@ -33,10 +34,10 @@ public class AuthorController {
List<Book> books = author.getBooks(); List<Book> books = author.getBooks();
Collections.sort(books, (a, b) -> a.getSeries().compareTo(b.getSeries())); books.sort(Comparator.comparing(Book::getSeries));
model.addAttribute("author", author); model.addAttribute("author", author)
model.addAttribute("books", books); .addAttribute("books", books);
return "author"; return "author";
} }

View file

@ -12,7 +12,7 @@ import ru.redrise.marinesco.library.Book;
@Controller @Controller
@RequestMapping("/book") @RequestMapping("/book")
public class BookController { public class BookController {
private BookRepository bookRepository; private final BookRepository bookRepository;
public BookController(BookRepository bookRepository){ public BookController(BookRepository bookRepository){
this.bookRepository = bookRepository; this.bookRepository = bookRepository;
@ -25,6 +25,7 @@ public class BookController {
model.addAttribute("Error", "Not found"); model.addAttribute("Error", "Not found");
return "book"; return "book";
} }
model.addAttribute("book", book); model.addAttribute("book", book);
return "book"; return "book";
} }

View file

@ -19,8 +19,8 @@ import ru.redrise.marinesco.library.Book;
@RequestMapping("/search") @RequestMapping("/search")
public class SearchController { public class SearchController {
private BookRepository inpEntryRepository; private final BookRepository inpEntryRepository;
private AuthorRepository authorRepository; private final AuthorRepository authorRepository;
public SearchController(BookRepository bookRepository, AuthorRepository authorRepository){ public SearchController(BookRepository bookRepository, AuthorRepository authorRepository){
this.inpEntryRepository = bookRepository; this.inpEntryRepository = bookRepository;
@ -34,7 +34,7 @@ public class SearchController {
@RequestParam(value = "author", required = false) Boolean author, @RequestParam(value = "author", required = false) Boolean author,
Model model) { Model model) {
if (search.trim().equals("")) if (search.trim().isEmpty())
return "search"; return "search";
model.addAttribute("searchPattern", search); model.addAttribute("searchPattern", search);
@ -46,21 +46,21 @@ public class SearchController {
if (title != null){ if (title != null){
List<Book> books = inpEntryRepository.findByTitleContainingIgnoreCase(search); List<Book> books = inpEntryRepository.findByTitleContainingIgnoreCase(search);
if (books.size() != 0) if (!books.isEmpty())
model.addAttribute("books", books); model.addAttribute("books", books);
model.addAttribute("isTitle", true); model.addAttribute("isTitle", true);
} }
if (series != null){ if (series != null){
List<Book> bookSeries = inpEntryRepository.findBySeriesContainingIgnoreCase(search); List<Book> bookSeries = inpEntryRepository.findBySeriesContainingIgnoreCase(search);
if (bookSeries.size() != 0) if (!bookSeries.isEmpty())
model.addAttribute("series", bookSeries); model.addAttribute("series", bookSeries);
model.addAttribute("isSeries", true); model.addAttribute("isSeries", true);
} }
if (author != null){ if (author != null){
List<Author> authors = authorRepository.findByAuthorNameContainingIgnoreCase(search); List<Author> authors = authorRepository.findByAuthorNameContainingIgnoreCase(search);
if (authors.size() != 0) if (!authors.isEmpty())
model.addAttribute("authors", authors); model.addAttribute("authors", authors);
model.addAttribute("isAuthor", true); model.addAttribute("isAuthor", true);
} }

View file

@ -8,7 +8,7 @@ import ru.redrise.marinesco.data.RolesRepository;
@Component @Component
public class AthorityByIdConverter implements Converter<Long, UserRole>{ public class AthorityByIdConverter implements Converter<Long, UserRole>{
private RolesRepository rolesRepo; private final RolesRepository rolesRepo;
public AthorityByIdConverter(RolesRepository rolesRepo){ public AthorityByIdConverter(RolesRepository rolesRepo){
this.rolesRepo = rolesRepo; this.rolesRepo = rolesRepo;

View file

@ -40,7 +40,7 @@ public class ManageUsersController {
} }
@ModelAttribute(name = "userGenerified") @ModelAttribute(name = "userGenerified")
public UserGenerified taco() { public UserGenerified createUserGenerified() {
return new UserGenerified(); return new UserGenerified();
} }
@ -92,7 +92,7 @@ public class ManageUsersController {
if (user == null) if (user == null)
return "redirect:/settings/manage_users"; return "redirect:/settings/manage_users";
user.setAuthorities(userGenerified.getAthorities()); user.setAuthorities(userGenerified.getAuthorities());
user.setDisplayname(userGenerified.getDisplayName()); user.setDisplayname(userGenerified.getDisplayName());
String password = userGenerified.getPassword().trim(); String password = userGenerified.getPassword().trim();
if (! password.trim().isEmpty()) if (! password.trim().isEmpty())
@ -114,7 +114,7 @@ public class ManageUsersController {
} }
User user = userRepository.save(form.toUser(passwordEncoder)); User user = userRepository.save(form.toUser(passwordEncoder));
log.info("Added user {} {} {}", user.getId(), user.getUsername(), user.getDisplayname(), log.info("Added user {} {} {} {}", user.getId(), user.getUsername(), user.getDisplayname(),
user.getAuthorities().get(0)); user.getAuthorities().get(0));
// Reloads page therefore new records appears // Reloads page therefore new records appears
return "redirect:/settings/manage_users"; return "redirect:/settings/manage_users";

View file

@ -21,12 +21,12 @@ import ru.redrise.marinesco.settings.ApplicationSettings;
@Controller @Controller
@RequestMapping("/register") @RequestMapping("/register")
public class RegistrationController { public class RegistrationController {
private UserRepository userRepo; private final UserRepository userRepo;
private RolesRepository rolesRepo; private final RolesRepository rolesRepo;
private PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private HttpServletRequest request; private final HttpServletRequest request;
private ApplicationSettings applicationSettings; private final ApplicationSettings applicationSettings;
public RegistrationController(UserRepository userRepo, public RegistrationController(UserRepository userRepo,
RolesRepository rolesRepo, RolesRepository rolesRepo,
@ -54,10 +54,10 @@ public class RegistrationController {
@PostMapping @PostMapping
public String postMethodName(@Valid RegistrationForm form, Errors errors, Model model) { public String postMethodName(@Valid RegistrationForm form, Errors errors, Model model) {
if (!applicationSettings.isRegistrationAllowed()) if (! applicationSettings.isRegistrationAllowed())
return "redirect:/"; return "redirect:/";
if (form.isPasswordsNotEqual()) { if (form.isPasswordsNotEqual()) {
model.addAttribute("passwordsMismatch", "Passwords must be the same."); model.addAttribute("passwordsMismatch", "Passwords must be the same");
return "registration"; return "registration";
} }
if (userRepo.findByUsername(form.getUsername()) != null){ if (userRepo.findByUsername(form.getUsername()) != null){

View file

@ -29,14 +29,10 @@ public class UserRole implements GrantedAuthority{
if (type == null) if (type == null)
throw new UnsupportedOperationException("Unimplemented method 'getAuthority'"); throw new UnsupportedOperationException("Unimplemented method 'getAuthority'");
switch (type) { return switch (type) {
case USER: case USER -> "ROLE_USER";
return "ROLE_USER"; case ADMIN -> "ROLE_ADMIN";
case ADMIN: };
return "ROLE_ADMIN";
default:
throw new UnsupportedOperationException("Unimplemented method 'getAuthority'");
}
} }
public enum Type{ public enum Type{

View file

@ -60,19 +60,12 @@ public class UserSettingsController {
Model model){ Model model){
if (errors.hasErrors()) if (errors.hasErrors())
return "user_settings"; return "user_settings";
if (! user.getDisplayname().equals(userSettingsForm.getDisplayname()))
user.setDisplayname(userSettingsForm.getDisplayname());
if (userSettingsForm.isNewPasswordSet()){
if (userSettingsForm.isNewPasswordValid()){
user.setPassword(passwordEncoder.encode(userSettingsForm.getNewPassword()));
}
else{
model.addAttribute("password_incorrect", "Password must be at least 8 characters long. Should not exceed 32 characters.");
return "user_settings";
}
}
log.info("{} {}", userSettingsForm.getDisplayname(), userSettingsForm.getNewPassword()); user.setDisplayname(userSettingsForm.getDisplayname());
if (userSettingsForm.isNewPasswordSet())
user.setPassword(passwordEncoder.encode(userSettingsForm.getNewPassword()));
userRepo.save(user); userRepo.save(user);
return "redirect:/profile/settings"; return "redirect:/profile/settings";

View file

@ -18,8 +18,8 @@ import ru.redrise.marinesco.library.RepackZip;
public class SettingsController { public class SettingsController {
private ApplicationSettings applicationSettings; private ApplicationSettings applicationSettings;
private InpxScanner inpxScanner; private final InpxScanner inpxScanner;
private RepackZip repackZip; private final RepackZip repackZip;
public SettingsController(ApplicationSettings applicationSettings, public SettingsController(ApplicationSettings applicationSettings,
InpxScanner inpxScanner, InpxScanner inpxScanner,

View file

@ -9,7 +9,7 @@ import ru.redrise.marinesco.security.UserRole;
@Component @Component
public class RoleByIdConverter implements Converter<Long, UserRole>{ public class RoleByIdConverter implements Converter<Long, UserRole>{
private RolesRepository rolesRepository; private final RolesRepository rolesRepository;
public RoleByIdConverter(RolesRepository rolesRepository){ public RoleByIdConverter(RolesRepository rolesRepository){
this.rolesRepository = rolesRepository; this.rolesRepository = rolesRepository;

View file

@ -22,10 +22,10 @@
<input type="text" name="displayname" id="displayname" th:value="${userSettingsForm.displayname}" <input type="text" name="displayname" id="displayname" th:value="${userSettingsForm.displayname}"
size="50%" /><br /> size="50%" /><br />
<span class="validationError" th:if="${password_incorrect} != null" <span class="validationError" th:if="${#fields.hasErrors('newPassword')}"
th:text="${password_incorrect}">false</span> th:errors="*{newPassword}">Error</span>
<br /> <br />
<label for="password">New password: </label> <label for="newPassword">New password: </label>
<br /> <br />
<input type="password" name="newPassword" id="newPassword" size="50%" /><br /> <input type="password" name="newPassword" id="newPassword" size="50%" /><br />