From 7b61b2071e0714b8b292afae8ec964ace5b62cc4 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Fri, 28 Nov 2025 23:33:27 -0300 Subject: [PATCH 1/3] Implement Genre Management with CRUD Operations - Add `Genre` entity with JPA annotations and database mapping. - Create repository, service, and delegate handlers for Genre CRUD operations. - Register handlers (`create_genre`, `get_genres`, `get_genre_by_id`, `update_genre`, `delete_genre`) in `DelegateActionManager`. - Introduce Protobuf definitions for Genre messages. - Update `DatabaseManager` to expose `EntityManager`. - Enhance `DelegateActionManager` constructor to use `EntityManager` for handler initialization. --- .../mediamanager/MediaManagerApplication.java | 2 +- .../com/mediamanager/mapper/GenreMapper.java | 34 ++++++ .../java/com/mediamanager/model/Genre.java | 30 ++++++ .../repository/GenreRepository.java | 101 ++++++++++++++++++ .../service/database/DatabaseManager.java | 4 + .../delegate/DelegateActionManager.java | 20 +++- .../handler/genre/CreateGenreHandler.java | 60 +++++++++++ .../handler/genre/DeleteGenreHandler.java | 70 ++++++++++++ .../handler/genre/GetGenreByIdHandler.java | 64 +++++++++++ .../handler/genre/GetGenreHandler.java | 54 ++++++++++ .../handler/genre/UpdateGenreHandler.java | 71 ++++++++++++ .../service/genre/GenreService.java | 88 +++++++++++++++ src/main/proto/genre.proto | 53 +++++++++ 13 files changed, 647 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/mediamanager/mapper/GenreMapper.java create mode 100644 src/main/java/com/mediamanager/model/Genre.java create mode 100644 src/main/java/com/mediamanager/repository/GenreRepository.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/genre/CreateGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/genre/DeleteGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreByIdHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/genre/UpdateGenreHandler.java create mode 100644 src/main/java/com/mediamanager/service/genre/GenreService.java create mode 100644 src/main/proto/genre.proto diff --git a/src/main/java/com/mediamanager/MediaManagerApplication.java b/src/main/java/com/mediamanager/MediaManagerApplication.java index b9579dc..3a85f6f 100644 --- a/src/main/java/com/mediamanager/MediaManagerApplication.java +++ b/src/main/java/com/mediamanager/MediaManagerApplication.java @@ -60,7 +60,7 @@ public class MediaManagerApplication { default: } databaseManager.init(); - actionManager = new DelegateActionManager(); + actionManager = new DelegateActionManager(databaseManager.getEntityManager()); actionManager.start(); ipcManager = new IPCManager(config,actionManager); ipcManager.init(); diff --git a/src/main/java/com/mediamanager/mapper/GenreMapper.java b/src/main/java/com/mediamanager/mapper/GenreMapper.java new file mode 100644 index 0000000..ceae850 --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/GenreMapper.java @@ -0,0 +1,34 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.Genre; +import com.mediamanager.protocol.messages.GenreMessages; + +public class GenreMapper { + public static GenreMessages.Genre toProtobuf(Genre entity) { + if (entity == null) { + return null; + } + + return GenreMessages.Genre.newBuilder() + .setId(entity.getId()) + .setName(entity.getName()) + .build(); + } + public static Genre toEntity(GenreMessages.Genre protobuf) { + if (protobuf == null) { + return null; + } + + Genre entity = new Genre(); + + // Só seta ID se for > 0 (protobuf default é 0) + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + + entity.setName(protobuf.getName()); + + return entity; + } +} + diff --git a/src/main/java/com/mediamanager/model/Genre.java b/src/main/java/com/mediamanager/model/Genre.java new file mode 100644 index 0000000..b2a736b --- /dev/null +++ b/src/main/java/com/mediamanager/model/Genre.java @@ -0,0 +1,30 @@ +package com.mediamanager.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "genre") +public class Genre { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(nullable = false) + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/com/mediamanager/repository/GenreRepository.java b/src/main/java/com/mediamanager/repository/GenreRepository.java new file mode 100644 index 0000000..6279caa --- /dev/null +++ b/src/main/java/com/mediamanager/repository/GenreRepository.java @@ -0,0 +1,101 @@ +package com.mediamanager.repository; + +import com.mediamanager.model.Genre; +import jakarta.persistence.EntityManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +/** + * Repository para acesso a dados de Genre + * Encapsula todas as operações de banco de dados + */ +public class GenreRepository { + private static final Logger logger = LogManager.getLogger(GenreRepository.class); + + private final EntityManager entityManager; + + public GenreRepository(EntityManager entityManager) { + this.entityManager = entityManager; + } + + /** + * Salva um novo genre + */ + public Genre save(Genre genre) { + logger.debug("Saving genre: {}", genre.getName()); + entityManager.getTransaction().begin(); + try { + entityManager.persist(genre); + entityManager.getTransaction().commit(); + logger.debug("Genre saved with ID: {}", genre.getId()); + return genre; + } catch (Exception e) { + entityManager.getTransaction().rollback(); + logger.error("Error saving genre", e); + throw e; + } + } + + /** + * Busca todos os genres + */ + public List findAll() { + logger.debug("Finding all genres"); + return entityManager + .createQuery("SELECT g FROM Genre g ORDER BY g.name", Genre.class) + .getResultList(); + } + + /** + * Busca genre por ID + */ + public Optional findById(Integer id) { + logger.debug("Finding genre by ID: {}", id); + Genre genre = entityManager.find(Genre.class, id); + return Optional.ofNullable(genre); + } + + /** + * Atualiza um genre existente + */ + public Genre update(Genre genre) { + logger.debug("Updating genre ID: {}", genre.getId()); + entityManager.getTransaction().begin(); + try { + Genre updated = entityManager.merge(genre); + entityManager.getTransaction().commit(); + logger.debug("Genre updated successfully"); + return updated; + } catch (Exception e) { + entityManager.getTransaction().rollback(); + logger.error("Error updating genre", e); + throw e; + } + } + + /** + * Deleta um genre por ID + */ + public boolean deleteById(Integer id) { + logger.debug("Deleting genre by ID: {}", id); + entityManager.getTransaction().begin(); + try { + Genre genre = entityManager.find(Genre.class, id); + if (genre == null) { + entityManager.getTransaction().rollback(); + return false; + } + entityManager.remove(genre); + entityManager.getTransaction().commit(); + logger.debug("Genre deleted successfully"); + return true; + } catch (Exception e) { + entityManager.getTransaction().rollback(); + logger.error("Error deleting genre", e); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/database/DatabaseManager.java b/src/main/java/com/mediamanager/service/database/DatabaseManager.java index 4c0375b..2d9c7eb 100644 --- a/src/main/java/com/mediamanager/service/database/DatabaseManager.java +++ b/src/main/java/com/mediamanager/service/database/DatabaseManager.java @@ -146,4 +146,8 @@ public abstract class DatabaseManager { logger.info("Hibernate ORM initialized successfully"); } + + public EntityManager getEntityManager() { + return entityManager; + } } \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index 5909c9c..d05de4e 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -2,9 +2,13 @@ package com.mediamanager.service.delegate; import com.google.protobuf.ByteString; import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.repository.GenreRepository; import com.mediamanager.service.delegate.handler.CloseHandler; import com.mediamanager.service.delegate.handler.EchoHandler; import com.mediamanager.service.delegate.handler.HeartbeatHandler; +import com.mediamanager.service.delegate.handler.genre.*; +import com.mediamanager.service.genre.GenreService; +import jakarta.persistence.EntityManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,9 +19,14 @@ public class DelegateActionManager { private static final Logger logger = LogManager.getLogger(DelegateActionManager.class); private final Map handlerRegistry; - - public DelegateActionManager() { - + private final EntityManager entityManager; + private final GenreService genreService; + + public DelegateActionManager(EntityManager entityManager) { + this.entityManager = entityManager; + + GenreRepository genreRepository = new GenreRepository(entityManager); + this.genreService = new GenreService(genreRepository); logger.debug("DelegateActionManager created"); this.handlerRegistry = new HashMap<>(); registerHandlers(); @@ -27,6 +36,11 @@ public class DelegateActionManager { handlerRegistry.put("echo",new EchoHandler()); handlerRegistry.put("heartbeat",new HeartbeatHandler()); handlerRegistry.put("close", new CloseHandler()); + handlerRegistry.put("create_genre", new CreateGenreHandler(genreService)); + handlerRegistry.put("get_genres", new GetGenreHandler(genreService)); + handlerRegistry.put("get_genre_by_id", new GetGenreByIdHandler(genreService)); + handlerRegistry.put("update_genre", new UpdateGenreHandler(genreService)); + handlerRegistry.put("delete_genre", new DeleteGenreHandler(genreService)); } public void start(){ diff --git a/src/main/java/com/mediamanager/service/delegate/handler/genre/CreateGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/genre/CreateGenreHandler.java new file mode 100644 index 0000000..93b530a --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/CreateGenreHandler.java @@ -0,0 +1,60 @@ +package com.mediamanager.service.delegate.handler.genre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.GenreMapper; +import com.mediamanager.model.Genre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.genre.GenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CreateGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateGenreHandler.class); + + private final GenreService genreService; + + public CreateGenreHandler(GenreService genreService) { + this.genreService = genreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + // 1. Parse protobuf request + GenreMessages.CreateGenreRequest createRequest = + GenreMessages.CreateGenreRequest.parseFrom(requestPayload); + + // 2. Chama service (lógica de negócio) + Genre genre = genreService.createGenre(createRequest.getName()); + + // 3. Converte entity para protobuf + GenreMessages.Genre genreProto = GenreMapper.toProtobuf(genre); + + // 4. Cria response protobuf + GenreMessages.CreateGenreResponse createResponse = + GenreMessages.CreateGenreResponse.newBuilder() + .setGenre(genreProto) + .build(); + + // 5. Retorna + return TransportProtocol.Response.newBuilder() + .setPayload(createResponse.toByteString()); + + } catch (IllegalArgumentException e) { + logger.error("Validation error", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(400) + .setPayload(ByteString.copyFromUtf8("Validation error: " + e.getMessage())); + + } catch (Exception e) { + logger.error("Error creating genre", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/genre/DeleteGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/genre/DeleteGenreHandler.java new file mode 100644 index 0000000..61f90df --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/DeleteGenreHandler.java @@ -0,0 +1,70 @@ +package com.mediamanager.service.delegate.handler.genre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.genre.GenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DeleteGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteGenreHandler.class); + + private final GenreService genreService; + + public DeleteGenreHandler(GenreService genreService) { + this.genreService = genreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + // 1. Parse protobuf request + GenreMessages.DeleteGenreRequest deleteRequest = + GenreMessages.DeleteGenreRequest.parseFrom(requestPayload); + + int id = deleteRequest.getId(); + + // 2. Deleta via service + boolean success = genreService.deleteGenre(id); + + // 3. Cria response + GenreMessages.DeleteGenreResponse deleteResponse; + + if (success) { + deleteResponse = GenreMessages.DeleteGenreResponse.newBuilder() + .setSuccess(true) + .setMessage("Genre deleted successfully") + .build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + } else { + deleteResponse = GenreMessages.DeleteGenreResponse.newBuilder() + .setSuccess(false) + .setMessage("Genre not found") + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + + } catch (Exception e) { + logger.error("Error deleting genre", e); + + GenreMessages.DeleteGenreResponse deleteResponse = + GenreMessages.DeleteGenreResponse.newBuilder() + .setSuccess(false) + .setMessage("Error: " + e.getMessage()) + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(deleteResponse.toByteString()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreByIdHandler.java new file mode 100644 index 0000000..77ce9e3 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreByIdHandler.java @@ -0,0 +1,64 @@ +package com.mediamanager.service.delegate.handler.genre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.GenreMapper; +import com.mediamanager.model.Genre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.genre.GenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +public class GetGenreByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetGenreByIdHandler.class); + + private final GenreService genreService; + + public GetGenreByIdHandler(GenreService genreService) { + this.genreService = genreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + // 1. Parse protobuf request + GenreMessages.GetGenreByIdRequest getByIdRequest = + GenreMessages.GetGenreByIdRequest.parseFrom(requestPayload); + + int id = getByIdRequest.getId(); + + // 2. Busca via service + Optional genreOpt = genreService.getGenreById(id); + + if (genreOpt.isEmpty()) { + logger.warn("Genre not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("Genre not found")); + } + + // 3. Converte para protobuf + GenreMessages.Genre genreProto = GenreMapper.toProtobuf(genreOpt.get()); + + GenreMessages.GetGenreByIdResponse getByIdResponse = + GenreMessages.GetGenreByIdResponse.newBuilder() + .setGenre(genreProto) + .build(); + + // 4. Retorna + return TransportProtocol.Response.newBuilder() + .setPayload(getByIdResponse.toByteString()); + + } catch (Exception e) { + logger.error("Error getting genre by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreHandler.java new file mode 100644 index 0000000..1cb3c94 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreHandler.java @@ -0,0 +1,54 @@ +package com.mediamanager.service.delegate.handler.genre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.GenreMapper; +import com.mediamanager.model.Genre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.genre.GenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + +public class GetGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetGenreHandler.class); + + private final GenreService genreService; + + public GetGenreHandler(GenreService genreService) { + this.genreService = genreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + // 1. Busca todos os genres via service + List genres = genreService.getAllGenres(); + + // 2. Converte cada Genre para protobuf + GenreMessages.GetGenresResponse.Builder responseBuilder = + GenreMessages.GetGenresResponse.newBuilder(); + + for (Genre genre : genres) { + GenreMessages.Genre genreProto = GenreMapper.toProtobuf(genre); + responseBuilder.addGenres(genreProto); + } + + GenreMessages.GetGenresResponse getGenresResponse = responseBuilder.build(); + + // 3. Retorna + return TransportProtocol.Response.newBuilder() + .setPayload(getGenresResponse.toByteString()); + + } catch (Exception e) { + logger.error("Error getting genres", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/genre/UpdateGenreHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/genre/UpdateGenreHandler.java new file mode 100644 index 0000000..919f50c --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/UpdateGenreHandler.java @@ -0,0 +1,71 @@ +package com.mediamanager.service.delegate.handler.genre; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.GenreMapper; +import com.mediamanager.model.Genre; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.delegate.ActionHandler; +import com.mediamanager.service.genre.GenreService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +public class UpdateGenreHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(UpdateGenreHandler.class); + + private final GenreService genreService; + + public UpdateGenreHandler(GenreService genreService) { + this.genreService = genreService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + // 1. Parse protobuf request + GenreMessages.UpdateGenreRequest updateRequest = + GenreMessages.UpdateGenreRequest.parseFrom(requestPayload); + + int id = updateRequest.getId(); + String newName = updateRequest.getName(); + + // 2. Atualiza via service + Optional genreOpt = genreService.updateGenre(id, newName); + + if (genreOpt.isEmpty()) { + logger.warn("Genre not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("Genre not found")); + } + + // 3. Converte para protobuf + GenreMessages.Genre genreProto = GenreMapper.toProtobuf(genreOpt.get()); + + GenreMessages.UpdateGenreResponse updateResponse = + GenreMessages.UpdateGenreResponse.newBuilder() + .setGenre(genreProto) + .build(); + + // 4. Retorna + return TransportProtocol.Response.newBuilder() + .setPayload(updateResponse.toByteString()); + + } catch (IllegalArgumentException e) { + logger.error("Validation error", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(400) + .setPayload(ByteString.copyFromUtf8("Validation error: " + e.getMessage())); + + } catch (Exception e) { + logger.error("Error updating genre", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/genre/GenreService.java b/src/main/java/com/mediamanager/service/genre/GenreService.java new file mode 100644 index 0000000..f0acf70 --- /dev/null +++ b/src/main/java/com/mediamanager/service/genre/GenreService.java @@ -0,0 +1,88 @@ +package com.mediamanager.service.genre; + +import com.mediamanager.model.Genre; +import com.mediamanager.repository.GenreRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +/** + * Service para lógica de negócio relacionada a Genre + */ +public class GenreService { + private static final Logger logger = LogManager.getLogger(GenreService.class); + + private final GenreRepository genreRepository; + + public GenreService(GenreRepository genreRepository) { + this.genreRepository = genreRepository; + } + + /** + * Cria um novo genre + */ + public Genre createGenre(String name) { + logger.info("Creating genre: {}", name); + + // Aqui poderia ter validações de negócio + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Genre name cannot be empty"); + } + + Genre genre = new Genre(); + genre.setName(name.trim()); + + return genreRepository.save(genre); + } + + /** + * Busca todos os genres + */ + public List getAllGenres() { + logger.info("Getting all genres"); + return genreRepository.findAll(); + } + + /** + * Busca genre por ID + */ + public Optional getGenreById(Integer id) { + logger.info("Getting genre by ID: {}", id); + return genreRepository.findById(id); + } + + /** + * Atualiza um genre existente + */ + public Optional updateGenre(Integer id, String name) { + logger.info("Updating genre ID {}: {}", id, name); + + // Validação + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Genre name cannot be empty"); + } + + Optional existingGenre = genreRepository.findById(id); + + if (existingGenre.isEmpty()) { + logger.warn("Genre not found with ID: {}", id); + return Optional.empty(); + } + + Genre genre = existingGenre.get(); + genre.setName(name.trim()); + + Genre updated = genreRepository.update(genre); + return Optional.of(updated); + } + + /** + * Deleta um genre por ID + */ + public boolean deleteGenre(Integer id) { + logger.info("Deleting genre ID: {}", id); + return genreRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/proto/genre.proto b/src/main/proto/genre.proto new file mode 100644 index 0000000..b059b89 --- /dev/null +++ b/src/main/proto/genre.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "GenreMessages"; + +package mediamanager.messages; + +message Genre { + int32 id = 1; + string name = 2; +} + +message CreateGenreRequest { + string name = 1; +} + +message CreateGenreResponse { + Genre genre = 1; +} + +message GetGenresRequest { + +} + +message GetGenresResponse { + repeated Genre genres = 1; +} + +message GetGenreByIdRequest { + int32 id = 1; +} + +message GetGenreByIdResponse { + Genre genre = 1; +} + +message UpdateGenreRequest { + int32 id = 1; + string name = 2; // Novo nome +} + +message UpdateGenreResponse { + Genre genre = 1; // Genre atualizado +} + +message DeleteGenreRequest { + int32 id = 1; +} + +message DeleteGenreResponse { + bool success = 1; + string message = 2; +} From 32fff9c725beff5ed933f1af47e89bb7d40330ac Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Sat, 29 Nov 2025 02:50:07 -0300 Subject: [PATCH 2/3] Add null-safe ID handling in `GenreMapper` and fix minor logging format in `IPCManager` - Ensure `GenreMapper` sets ID only if it's non-null and valid (> 0) to prevent potential NPEs. - Adjust logging format in `IPCManager` for consistent indentation. --- .../java/com/mediamanager/mapper/GenreMapper.java | 14 ++++++++++---- .../com/mediamanager/service/ipc/IPCManager.java | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mediamanager/mapper/GenreMapper.java b/src/main/java/com/mediamanager/mapper/GenreMapper.java index ceae850..331e67c 100644 --- a/src/main/java/com/mediamanager/mapper/GenreMapper.java +++ b/src/main/java/com/mediamanager/mapper/GenreMapper.java @@ -9,10 +9,16 @@ public class GenreMapper { return null; } - return GenreMessages.Genre.newBuilder() - .setId(entity.getId()) - .setName(entity.getName()) - .build(); + GenreMessages.Genre.Builder builder = GenreMessages.Genre.newBuilder() + .setName(entity.getName()); + + // Only set ID when it's present and valid (> 0). Avoids NPE for null IDs. + Integer id = entity.getId(); + if (id != null && id > 0) { + builder.setId(id); + } + + return builder.build(); } public static Genre toEntity(GenreMessages.Genre protobuf) { if (protobuf == null) { diff --git a/src/main/java/com/mediamanager/service/ipc/IPCManager.java b/src/main/java/com/mediamanager/service/ipc/IPCManager.java index 2115066..7fe7541 100644 --- a/src/main/java/com/mediamanager/service/ipc/IPCManager.java +++ b/src/main/java/com/mediamanager/service/ipc/IPCManager.java @@ -125,7 +125,7 @@ public class IPCManager { } - logger.info("Closing IPC connection..."); + logger.info("Closing IPC connection..."); running.set(false); if (serverChannel != null && serverChannel.isOpen()) { From f70c1e1d947f3bd3ed5863018312cd7650e22373 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Sat, 29 Nov 2025 03:02:49 -0300 Subject: [PATCH 3/3] Switch from `EntityManager` to `EntityManagerFactory` across repositories, managers, and services for improved resource management and thread safety. --- .../mediamanager/MediaManagerApplication.java | 2 +- .../repository/GenreRepository.java | 64 ++++++++++++------- .../service/database/DatabaseManager.java | 15 +---- .../delegate/DelegateActionManager.java | 10 +-- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/mediamanager/MediaManagerApplication.java b/src/main/java/com/mediamanager/MediaManagerApplication.java index 3a85f6f..22834fb 100644 --- a/src/main/java/com/mediamanager/MediaManagerApplication.java +++ b/src/main/java/com/mediamanager/MediaManagerApplication.java @@ -60,7 +60,7 @@ public class MediaManagerApplication { default: } databaseManager.init(); - actionManager = new DelegateActionManager(databaseManager.getEntityManager()); + actionManager = new DelegateActionManager(databaseManager.getEntityManagerFactory()); actionManager.start(); ipcManager = new IPCManager(config,actionManager); ipcManager.init(); diff --git a/src/main/java/com/mediamanager/repository/GenreRepository.java b/src/main/java/com/mediamanager/repository/GenreRepository.java index 6279caa..73e9be9 100644 --- a/src/main/java/com/mediamanager/repository/GenreRepository.java +++ b/src/main/java/com/mediamanager/repository/GenreRepository.java @@ -2,6 +2,7 @@ package com.mediamanager.repository; import com.mediamanager.model.Genre; import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,10 +16,10 @@ import java.util.Optional; public class GenreRepository { private static final Logger logger = LogManager.getLogger(GenreRepository.class); - private final EntityManager entityManager; + private final EntityManagerFactory entityManagerFactory; - public GenreRepository(EntityManager entityManager) { - this.entityManager = entityManager; + public GenreRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; } /** @@ -26,16 +27,19 @@ public class GenreRepository { */ public Genre save(Genre genre) { logger.debug("Saving genre: {}", genre.getName()); - entityManager.getTransaction().begin(); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); try { - entityManager.persist(genre); - entityManager.getTransaction().commit(); + em.persist(genre); + em.getTransaction().commit(); logger.debug("Genre saved with ID: {}", genre.getId()); return genre; } catch (Exception e) { - entityManager.getTransaction().rollback(); + em.getTransaction().rollback(); logger.error("Error saving genre", e); throw e; + } finally { + if (em.isOpen()) em.close(); } } @@ -44,9 +48,14 @@ public class GenreRepository { */ public List findAll() { logger.debug("Finding all genres"); - return entityManager - .createQuery("SELECT g FROM Genre g ORDER BY g.name", Genre.class) - .getResultList(); + EntityManager em = entityManagerFactory.createEntityManager(); + try { + return em + .createQuery("SELECT g FROM Genre g ORDER BY g.name", Genre.class) + .getResultList(); + } finally { + if (em.isOpen()) em.close(); + } } /** @@ -54,8 +63,13 @@ public class GenreRepository { */ public Optional findById(Integer id) { logger.debug("Finding genre by ID: {}", id); - Genre genre = entityManager.find(Genre.class, id); - return Optional.ofNullable(genre); + EntityManager em = entityManagerFactory.createEntityManager(); + try { + Genre genre = em.find(Genre.class, id); + return Optional.ofNullable(genre); + } finally { + if (em.isOpen()) em.close(); + } } /** @@ -63,16 +77,19 @@ public class GenreRepository { */ public Genre update(Genre genre) { logger.debug("Updating genre ID: {}", genre.getId()); - entityManager.getTransaction().begin(); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); try { - Genre updated = entityManager.merge(genre); - entityManager.getTransaction().commit(); + Genre updated = em.merge(genre); + em.getTransaction().commit(); logger.debug("Genre updated successfully"); return updated; } catch (Exception e) { - entityManager.getTransaction().rollback(); + em.getTransaction().rollback(); logger.error("Error updating genre", e); throw e; + } finally { + if (em.isOpen()) em.close(); } } @@ -81,21 +98,24 @@ public class GenreRepository { */ public boolean deleteById(Integer id) { logger.debug("Deleting genre by ID: {}", id); - entityManager.getTransaction().begin(); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); try { - Genre genre = entityManager.find(Genre.class, id); + Genre genre = em.find(Genre.class, id); if (genre == null) { - entityManager.getTransaction().rollback(); + em.getTransaction().rollback(); return false; } - entityManager.remove(genre); - entityManager.getTransaction().commit(); + em.remove(genre); + em.getTransaction().commit(); logger.debug("Genre deleted successfully"); return true; } catch (Exception e) { - entityManager.getTransaction().rollback(); + em.getTransaction().rollback(); logger.error("Error deleting genre", e); throw e; + } finally { + if (em.isOpen()) em.close(); } } } \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/database/DatabaseManager.java b/src/main/java/com/mediamanager/service/database/DatabaseManager.java index 2d9c7eb..13188a0 100644 --- a/src/main/java/com/mediamanager/service/database/DatabaseManager.java +++ b/src/main/java/com/mediamanager/service/database/DatabaseManager.java @@ -1,6 +1,5 @@ package com.mediamanager.service.database; -import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,7 +19,6 @@ public abstract class DatabaseManager { protected Connection connection; protected String connectionUrl; protected EntityManagerFactory entityManagerFactory; - protected EntityManager entityManager; protected static final Logger logger = LogManager.getLogger(DatabaseManager.class); public DatabaseManager(Properties config) { @@ -48,14 +46,6 @@ public abstract class DatabaseManager { } public void close() { - if (entityManager != null && entityManager.isOpen()) { - try { - entityManager.close(); - logger.info("EntityManager closed"); - } catch (Exception e) { - logger.error("Error closing EntityManager: {}", e.getMessage()); - } - } if (entityManagerFactory != null && entityManagerFactory.isOpen()) { try { entityManagerFactory.close(); @@ -138,7 +128,6 @@ public abstract class DatabaseManager { try { entityManagerFactory = hibernateConfig.buildSessionFactory().unwrap(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); } catch (Exception e) { logger.error("Failed to initialize Hibernate: {}", e.getMessage()); throw new RuntimeException("Hibernate initialization failed", e); @@ -147,7 +136,7 @@ public abstract class DatabaseManager { logger.info("Hibernate ORM initialized successfully"); } - public EntityManager getEntityManager() { - return entityManager; + public EntityManagerFactory getEntityManagerFactory() { + return entityManagerFactory; } } \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index d05de4e..2221236 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -8,7 +8,7 @@ import com.mediamanager.service.delegate.handler.EchoHandler; import com.mediamanager.service.delegate.handler.HeartbeatHandler; import com.mediamanager.service.delegate.handler.genre.*; import com.mediamanager.service.genre.GenreService; -import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -19,13 +19,13 @@ public class DelegateActionManager { private static final Logger logger = LogManager.getLogger(DelegateActionManager.class); private final Map handlerRegistry; - private final EntityManager entityManager; + private final EntityManagerFactory entityManagerFactory; private final GenreService genreService; - public DelegateActionManager(EntityManager entityManager) { - this.entityManager = entityManager; + public DelegateActionManager(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; - GenreRepository genreRepository = new GenreRepository(entityManager); + GenreRepository genreRepository = new GenreRepository(entityManagerFactory); this.genreService = new GenreService(genreRepository); logger.debug("DelegateActionManager created"); this.handlerRegistry = new HashMap<>();