Compare commits

...
Author SHA1 Message Date
Gustavo Henrique Santos Souza de Miranda f70c1e1d94 Switch from EntityManager to EntityManagerFactory across repositories, managers, and services for improved resource management and thread safety. 2025-11-29 03:02:49 -03:00
Gustavo Henrique Santos Souza de Miranda 32fff9c725 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.
2025-11-29 02:50:07 -03:00
Gustavo Henrique Santos Souza de Miranda 7b61b2071e 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.
2025-11-28 23:33:27 -03:00
Gustavo Henrique Miranda ba53d4d8c4 Merge pull request #10 from gmbrax/refactor/add-hibernate-and-reflection-dependency
Remove `Media` entity and integrate Hibernate ORM into `DatabaseManager`:
2025-11-28 00:38:21 -03:00
Gustavo Henrique Santos Souza de Miranda 081a1f1ed4 Add exception handling for entity scanning in DatabaseManager
- Wrap entity scanning in a try-catch block to handle potential exceptions.
- Log descriptive error messages and throw a runtime exception for failure cases.
- Warn if no `@Entity` classes are found during the scan.
2025-11-27 19:20:22 -03:00
Gustavo Henrique Santos Souza de Miranda e395e21e8c Add validation for required Hibernate configuration properties in DatabaseManager
- Ensure non-null and non-empty values for `hibernate.dialect`, `database.driver`, and `connectionUrl`.
- Throw `IllegalStateException` when required properties are missing.
2025-11-27 19:08:11 -03:00
Gustavo Henrique Santos Souza de Miranda 412a590a98 Add exception handling for Hibernate initialization in DatabaseManager
- Wrap Hibernate setup in a try-catch block.
- Log errors during initialization failure with descriptive messages.
- Throw a runtime exception if setup fails to ensure proper error propagation.
2025-11-27 19:04:44 -03:00
Gustavo Henrique Santos Souza de Miranda bc0199312f Add null and open-state checks for EntityManager and EntityManagerFactory closure
- Ensure safe closure of `EntityManager` and `EntityManagerFactory`.
- Add logging for successful closure and error handling during exceptions.
2025-11-27 18:55:43 -03:00
Gustavo Henrique Santos Souza de Miranda 4ae1e3075d Remove Media entity and integrate Hibernate ORM into DatabaseManager:
- Deleted `Media` class and its annotations.
- Added Hibernate ORM initialization in `DatabaseManager`, including dynamic entity scanning and configuration.
- Updated `pom.xml` to include dependencies for Hibernate community dialects and Reflections.
2025-11-27 18:40:08 -03:00
Gustavo Henrique Miranda fc09c4e257 Merge pull request #9 from gmbrax/fix/fix-unsecure-postgres-version
Fix the pom.xml
2025-11-24 22:19:31 -03:00
Gustavo Henrique Santos Souza de Miranda 53004a3730 Fix the pom.xml to alter the postgres dependency to a safer version due a CVE in the 42.7.5 then it was bumped t0 42.7.7 2025-11-24 21:59:35 -03:00
17 changed files with 766 additions and 136 deletions
+12 -1
View File
@@ -13,7 +13,7 @@
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hibernate.version>7.1.7.Final</hibernate.version> <hibernate.version>7.1.7.Final</hibernate.version>
<postgresql.version>42.7.5</postgresql.version> <postgresql.version>42.7.7</postgresql.version>
<hikaricp.version>5.1.0</hikaricp.version> <hikaricp.version>5.1.0</hikaricp.version>
<log4j.version>2.23.1</log4j.version> <log4j.version>2.23.1</log4j.version>
<junit.version>5.10.2</junit.version> <junit.version>5.10.2</junit.version>
@@ -35,6 +35,12 @@
<version>${hibernate.version}</version> <version>${hibernate.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- HikariCP for connection pooling --> <!-- HikariCP for connection pooling -->
<dependency> <dependency>
<groupId>com.zaxxer</groupId> <groupId>com.zaxxer</groupId>
@@ -88,6 +94,11 @@
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version> <version>3.44.1.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -60,7 +60,7 @@ public class MediaManagerApplication {
default: default:
} }
databaseManager.init(); databaseManager.init();
actionManager = new DelegateActionManager(); actionManager = new DelegateActionManager(databaseManager.getEntityManagerFactory());
actionManager.start(); actionManager.start();
ipcManager = new IPCManager(config,actionManager); ipcManager = new IPCManager(config,actionManager);
ipcManager.init(); ipcManager.init();
@@ -0,0 +1,40 @@
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;
}
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) {
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;
}
}
@@ -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;
}
}
@@ -1,128 +0,0 @@
package com.mediamanager.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "media")
public class Media {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false, unique = true)
private String filePath;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private MediaType type;
@Column
private Long fileSize;
@Column
private String mimeType;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
// Constructors
public Media() {}
public Media(String title, String filePath, MediaType type) {
this.title = title;
this.filePath = filePath;
this.type = type;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public MediaType getType() {
return type;
}
public void setType(MediaType type) {
this.type = type;
}
public Long getFileSize() {
return fileSize;
}
public void setFileSize(Long fileSize) {
this.fileSize = fileSize;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
public enum MediaType {
VIDEO,
AUDIO,
IMAGE,
DOCUMENT,
OTHER
}
}
@@ -0,0 +1,121 @@
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;
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 EntityManagerFactory entityManagerFactory;
public GenreRepository(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
/**
* Salva um novo genre
*/
public Genre save(Genre genre) {
logger.debug("Saving genre: {}", genre.getName());
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
em.persist(genre);
em.getTransaction().commit();
logger.debug("Genre saved with ID: {}", genre.getId());
return genre;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error saving genre", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
/**
* Busca todos os genres
*/
public List<Genre> findAll() {
logger.debug("Finding all genres");
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();
}
}
/**
* Busca genre por ID
*/
public Optional<Genre> findById(Integer id) {
logger.debug("Finding genre by ID: {}", id);
EntityManager em = entityManagerFactory.createEntityManager();
try {
Genre genre = em.find(Genre.class, id);
return Optional.ofNullable(genre);
} finally {
if (em.isOpen()) em.close();
}
}
/**
* Atualiza um genre existente
*/
public Genre update(Genre genre) {
logger.debug("Updating genre ID: {}", genre.getId());
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
Genre updated = em.merge(genre);
em.getTransaction().commit();
logger.debug("Genre updated successfully");
return updated;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error updating genre", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
/**
* Deleta um genre por ID
*/
public boolean deleteById(Integer id) {
logger.debug("Deleting genre by ID: {}", id);
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
Genre genre = em.find(Genre.class, id);
if (genre == null) {
em.getTransaction().rollback();
return false;
}
em.remove(genre);
em.getTransaction().commit();
logger.debug("Genre deleted successfully");
return true;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error deleting genre", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
}
@@ -1,16 +1,24 @@
package com.mediamanager.service.database; package com.mediamanager.service.database;
import jakarta.persistence.EntityManagerFactory;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.hibernate.cfg.Configuration;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import jakarta.persistence.Entity;
import java.util.Set;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Properties; import java.util.Properties;
public abstract class DatabaseManager { public abstract class DatabaseManager {
protected final Properties configuration; protected final Properties configuration;
protected Connection connection; protected Connection connection;
protected String connectionUrl;
protected EntityManagerFactory entityManagerFactory;
protected static final Logger logger = LogManager.getLogger(DatabaseManager.class); protected static final Logger logger = LogManager.getLogger(DatabaseManager.class);
public DatabaseManager(Properties config) { public DatabaseManager(Properties config) {
@@ -38,6 +46,14 @@ public abstract class DatabaseManager {
} }
public void close() { public void close() {
if (entityManagerFactory != null && entityManagerFactory.isOpen()) {
try {
entityManagerFactory.close();
logger.info("EntityManagerFactory closed");
} catch (Exception e) {
logger.error("Error closing EntityManagerFactory: {}", e.getMessage());
}
}
if (connection != null) { if (connection != null) {
try { try {
logger.info("Closing database connection..."); logger.info("Closing database connection...");
@@ -50,6 +66,7 @@ public abstract class DatabaseManager {
logger.debug("No database connection to close"); logger.debug("No database connection to close");
} }
} }
protected boolean testConnection() { protected boolean testConnection() {
try (Statement stmt = connection.createStatement()) { try (Statement stmt = connection.createStatement()) {
stmt.execute("SELECT 1"); stmt.execute("SELECT 1");
@@ -59,4 +76,67 @@ public abstract class DatabaseManager {
return false; return false;
} }
} }
protected void initializeHibernate() {
logger.info("Initializing Hibernate ORM...");
Configuration hibernateConfig = new Configuration();
String dialect = configuration.getProperty("hibernate.dialect");
String hbm2ddl = configuration.getProperty("hibernate.hbm2ddl.auto");
String driver = configuration.getProperty("database.driver");
if (dialect == null || dialect.isEmpty()) {
throw new IllegalStateException("hibernate.dialect property is required but not configured");
}
if (driver == null || driver.isEmpty()) {
throw new IllegalStateException("database.driver property is required but not configured");
}
if (connectionUrl == null || connectionUrl.isEmpty()) {
throw new IllegalStateException("connectionUrl must be set before initializing Hibernate");
}
hibernateConfig.setProperty("hibernate.connection.url", connectionUrl);
hibernateConfig.setProperty("hibernate.connection.driver_class", driver);
hibernateConfig.setProperty("hibernate.dialect", dialect);
hibernateConfig.setProperty("hibernate.hbm2ddl.auto", hbm2ddl);
hibernateConfig.setProperty("hibernate.show_sql",
configuration.getProperty("hibernate.show_sql", "false"));
hibernateConfig.setProperty("hibernate.format_sql",
configuration.getProperty("hibernate.format_sql", "true"));
logger.info("Scanning for entities in package: com.mediamanager.model");
Set<Class<?>> entityClasses;
try {
Reflections reflections = new Reflections("com.mediamanager.model", Scanners.TypesAnnotated);
entityClasses = reflections.getTypesAnnotatedWith(Entity.class);
} catch (Exception e) {
logger.error("Failed to scan for entities: {}", e.getMessage());
throw new RuntimeException("Entity scanning failed", e);
}
logger.info("Found {} entities", entityClasses.size());
if (entityClasses.isEmpty()) {
logger.warn("No @Entity classes found in package com.mediamanager.model - is this expected?");
}
for (Class<?> entityClass : entityClasses) {
logger.debug("Registering entity: {}", entityClass.getSimpleName());
hibernateConfig.addAnnotatedClass(entityClass);
}
try {
entityManagerFactory = hibernateConfig.buildSessionFactory().unwrap(EntityManagerFactory.class);
} catch (Exception e) {
logger.error("Failed to initialize Hibernate: {}", e.getMessage());
throw new RuntimeException("Hibernate initialization failed", e);
}
logger.info("Hibernate ORM initialized successfully");
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
} }
@@ -16,7 +16,7 @@ public class SqliteDatabaseManager extends DatabaseManager {
private static final Logger logger = LogManager.getLogger(SqliteDatabaseManager.class); private static final Logger logger = LogManager.getLogger(SqliteDatabaseManager.class);
private String connectionUrl;
public SqliteDatabaseManager(Properties config) { public SqliteDatabaseManager(Properties config) {
super(config); super(config);
@@ -45,12 +45,14 @@ public class SqliteDatabaseManager extends DatabaseManager {
this.connectionUrl = "jdbc:sqlite:" + dbFile.toAbsolutePath().toString(); this.connectionUrl = "jdbc:sqlite:" + dbFile.toAbsolutePath().toString();
logger.info("Database file path: {}", dbFile); logger.info("Database file path: {}", dbFile);
logger.info("Connection URL: {}", this.connectionUrl); logger.info("Connection URL: {}", this.connectionUrl);
initializeHibernate();
this.connection = createConnection(); this.connection = createConnection();
configurePerformancePragmas(); configurePerformancePragmas();
performSanityChecks(); performSanityChecks();
ensureSchemaExists(); ensureSchemaExists();
logger.info("SQLite database initialized successfully"); logger.info("SQLite database initialized successfully");
} }
@Override @Override
@@ -2,9 +2,13 @@ package com.mediamanager.service.delegate;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.mediamanager.protocol.TransportProtocol; import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.repository.GenreRepository;
import com.mediamanager.service.delegate.handler.CloseHandler; import com.mediamanager.service.delegate.handler.CloseHandler;
import com.mediamanager.service.delegate.handler.EchoHandler; import com.mediamanager.service.delegate.handler.EchoHandler;
import com.mediamanager.service.delegate.handler.HeartbeatHandler; import com.mediamanager.service.delegate.handler.HeartbeatHandler;
import com.mediamanager.service.delegate.handler.genre.*;
import com.mediamanager.service.genre.GenreService;
import jakarta.persistence.EntityManagerFactory;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -15,9 +19,14 @@ public class DelegateActionManager {
private static final Logger logger = LogManager.getLogger(DelegateActionManager.class); private static final Logger logger = LogManager.getLogger(DelegateActionManager.class);
private final Map<String, ActionHandler> handlerRegistry; private final Map<String, ActionHandler> handlerRegistry;
private final EntityManagerFactory entityManagerFactory;
public DelegateActionManager() { private final GenreService genreService;
public DelegateActionManager(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
GenreRepository genreRepository = new GenreRepository(entityManagerFactory);
this.genreService = new GenreService(genreRepository);
logger.debug("DelegateActionManager created"); logger.debug("DelegateActionManager created");
this.handlerRegistry = new HashMap<>(); this.handlerRegistry = new HashMap<>();
registerHandlers(); registerHandlers();
@@ -27,6 +36,11 @@ public class DelegateActionManager {
handlerRegistry.put("echo",new EchoHandler()); handlerRegistry.put("echo",new EchoHandler());
handlerRegistry.put("heartbeat",new HeartbeatHandler()); handlerRegistry.put("heartbeat",new HeartbeatHandler());
handlerRegistry.put("close", new CloseHandler()); 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(){ public void start(){
@@ -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()));
}
}
}
@@ -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());
}
}
}
@@ -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<Genre> 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()));
}
}
}
@@ -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<Genre> 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()));
}
}
}
@@ -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<Genre> 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()));
}
}
}
@@ -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<Genre> getAllGenres() {
logger.info("Getting all genres");
return genreRepository.findAll();
}
/**
* Busca genre por ID
*/
public Optional<Genre> getGenreById(Integer id) {
logger.info("Getting genre by ID: {}", id);
return genreRepository.findById(id);
}
/**
* Atualiza um genre existente
*/
public Optional<Genre> 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<Genre> 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);
}
}
@@ -125,7 +125,7 @@ public class IPCManager {
} }
logger.info("Closing IPC connection..."); logger.info("Closing IPC connection...");
running.set(false); running.set(false);
if (serverChannel != null && serverChannel.isOpen()) { if (serverChannel != null && serverChannel.isOpen()) {
+53
View File
@@ -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;
}