Compare commits

...
Author SHA1 Message Date
Gustavo Henrique Santos Souza de Miranda 51a46c37e5 Rename DeleteArtistHandle to DeleteArtistHandler for consistency and update related references. Clean up Protobuf comment in UpdateArtistResponse. 2025-11-30 17:33:25 -03:00
Gustavo Henrique Santos Souza de Miranda 0df4b31a4d Fix logging messages in ArtistRepository for clarity
- Correct references from "genre" to "artist" in logging statements.
- Remove unused `Genre` import for cleanup.
2025-11-30 12:59:57 -03:00
Gustavo Henrique Mirandaandcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> 3c42fd9735 Update src/main/java/com/mediamanager/mapper/ArtistMapper.java
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-30 12:54:18 -03:00
Gustavo Henrique Santos Souza de Miranda 0a61f1f2fa Implement Artist Management with CRUD Operations
- Add `Artist` entity with JPA annotations and database mapping.
- Create repository, service, and delegate handlers for Artist CRUD operations.
- Register handlers (`artist.create`, `artist.getAll`, `artist.getById`, `artist.update`, `artist.delete`) in `DelegateActionManager`.
- Introduce Protobuf definitions for Artist messages.
- Update `initializeServices` in `DelegateActionManager` for Artist service support.
- Add null-safe ID handling in `ArtistMapper` to prevent potential NPEs.
2025-11-30 12:46:50 -03:00
Gustavo Henrique Miranda fecb0288d3 Merge pull request #12 from gmbrax/feature/auto-handler-scanner
Feature/auto handler scanner
2025-11-29 20:37:26 -03:00
Gustavo Henrique Santos Souza de Miranda 5ce8f4ca2a Improve DelegateActionManager error handling for invalid handlers
- Add validation to ensure classes annotated with `@Action` implement `ActionHandler`, throwing `IllegalArgumentException` for mismatches.
- Fix minor formatting issue in `@Action` annotation for `GetGenreByIdHandler`.
2025-11-29 15:33:08 -03:00
Gustavo Henrique Mirandaandcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> a5bd6e2c39 Update src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-29 09:52:54 -03:00
Gustavo Henrique Santos Souza de Miranda f87d6b1b53 Refactor DelegateActionManager for dynamic handler registration
- Replace manual handler registration with automatic scanning and registration of handlers annotated with `@Action`.
- Introduce `ServiceLocator` to enable dependency injection for dynamically instantiated handlers.
- Add `initializeServices` to centralize service initialization and registration.
- Enhance error handling and logging for handler instantiation and registration processes.
2025-11-29 08:26:58 -03:00
Gustavo Henrique Santos Souza de Miranda b715bb1bd3 Add @Action annotation and ServiceLocator for improved handler registration
- Introduce `@Action` annotation to simplify action handler identification and tagging.
- Add `ServiceLocator` for dynamic service registration and retrieval.
- Annotate handlers (`CreateGenreHandler`, `GetGenreHandler`, `UpdateGenreHandler`, `DeleteGenreHandler`, etc.) with corresponding actions.
2025-11-29 07:54:46 -03:00
Gustavo Henrique Miranda 9c3a1126a7 Merge pull request #11 from gmbrax/feature/implement-genre-management
Implement Genre Management with CRUD Operations
2025-11-29 06:09:44 -03:00
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
32 changed files with 1549 additions and 144 deletions
+12 -1
View File
@@ -13,7 +13,7 @@
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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>
<log4j.version>2.23.1</log4j.version>
<junit.version>5.10.2</junit.version>
@@ -35,6 +35,12 @@
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- HikariCP for connection pooling -->
<dependency>
<groupId>com.zaxxer</groupId>
@@ -88,6 +94,11 @@
<artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
</dependencies>
<build>
@@ -60,7 +60,7 @@ public class MediaManagerApplication {
default:
}
databaseManager.init();
actionManager = new DelegateActionManager();
actionManager = new DelegateActionManager(databaseManager.getEntityManagerFactory());
actionManager.start();
ipcManager = new IPCManager(config,actionManager);
ipcManager.init();
@@ -0,0 +1,41 @@
package com.mediamanager.mapper;
import com.mediamanager.model.Artist;
import com.mediamanager.protocol.messages.ArtistMessages;
public class ArtistMapper {
public static ArtistMessages.Artist toProtobuf(Artist entity){
if (entity == null) {
return null;
}
String name = entity.getName();
if (name == null) {
throw new IllegalArgumentException("Artist name cannot be null");
}
ArtistMessages.Artist.Builder builder = ArtistMessages.Artist.newBuilder()
.setName(name);
// 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 Artist toEntity(ArtistMessages.Artist protobuf) {
if (protobuf == null) {
return null;
}
Artist entity = new Artist();
if (protobuf.getId() > 0) {
entity.setId(protobuf.getId());
}
entity.setName(protobuf.getName());
return entity;
}
}
@@ -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,27 @@
package com.mediamanager.model;
import jakarta.persistence.*;
@Entity
@Table(name = "artist")
public class Artist {
@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;
}
}
@@ -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,104 @@
package com.mediamanager.repository;
import com.mediamanager.model.Artist;
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;
public class ArtistRepository {
private static final Logger logger = LogManager.getLogger(ArtistRepository.class);
private final EntityManagerFactory entityManagerFactory;
public ArtistRepository(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
public Artist save(Artist artist){
logger.debug("Saving Artist: {}", artist.getName());
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
em.persist(artist);
em.getTransaction().commit();
logger.debug("Artist saved with ID: {}", artist.getId());
return artist;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error saving Artist", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
public List<Artist> findAll(){
logger.debug("Finding all Artists");
EntityManager em = entityManagerFactory.createEntityManager();
try {
return em
.createQuery("SELECT a FROM Artist a ORDER BY a.name", Artist.class)
.getResultList();
} finally {
if (em.isOpen()) em.close();
}
}
public Optional<Artist> findById(Integer id){
logger.debug("Finding artist by ID: {}", id);
EntityManager em = entityManagerFactory.createEntityManager();
try {
Artist artist = em.find(Artist.class, id);
return Optional.ofNullable(artist);
} finally {
if (em.isOpen()) em.close();
}
}
public Artist update(Artist artist){
logger.debug("Updating artist ID: {}", artist.getId());
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
Artist updated = em.merge(artist);
em.getTransaction().commit();
logger.debug("Artist updated successfully");
return updated;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error updating artist", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
public boolean deleteById(Integer id){
logger.debug("Deleting Artist by ID: {}", id);
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
Artist artist = em.find(Artist.class, id);
if (artist == null) {
em.getTransaction().rollback();
return false;
}
em.remove(artist);
em.getTransaction().commit();
logger.debug("Artist deleted successfully");
return true;
} catch (Exception e) {
em.getTransaction().rollback();
logger.error("Error deleting artist", e);
throw e;
} finally {
if (em.isOpen()) em.close();
}
}
}
@@ -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();
}
}
}
@@ -0,0 +1,65 @@
package com.mediamanager.service.artist;
import com.mediamanager.model.Artist;
import com.mediamanager.repository.ArtistRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Optional;
public class ArtistService {
private static final Logger logger = LogManager.getLogger(ArtistService.class);
private final ArtistRepository artistRepository;
public ArtistService(ArtistRepository artistRepository) {
this.artistRepository = artistRepository;
}
public Artist createArtist(String name){
logger.info("Creating artist: {}", name);
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Artist name cannot be empty");
}
Artist artist = new Artist();
artist.setName(name.trim());
return artistRepository.save(artist);
}
public List<Artist> getAllArtists(){
logger.info("Getting all artists");
return artistRepository.findAll();
}
public Optional<Artist> getArtistById(Integer id){
logger.info("Getting artist by ID: {}", id);
return artistRepository.findById(id);
}
public Optional<Artist> updateArtist(Integer id, String name){
logger.info("Updating artist ID {}: {}", id, name);
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Artist name cannot be empty");
}
Optional<Artist> existingArtist = artistRepository.findById(id);
if(existingArtist.isEmpty()){
logger.warn("Artist not found with ID: {}", id);
return Optional.empty();
}
Artist artist = existingArtist.get();
artist.setName(name.trim());
Artist updatedArtist = artistRepository.update(artist);
return Optional.of(updatedArtist);
}
public boolean deleteArtist(Integer id){
logger.info("Deleting artist ID: {}", id);
return artistRepository.deleteById(id);
}
}
@@ -1,16 +1,24 @@
package com.mediamanager.service.database;
import jakarta.persistence.EntityManagerFactory;
import org.apache.logging.log4j.LogManager;
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.SQLException;
import java.sql.Statement;
import java.util.Properties;
public abstract class DatabaseManager {
protected final Properties configuration;
protected Connection connection;
protected String connectionUrl;
protected EntityManagerFactory entityManagerFactory;
protected static final Logger logger = LogManager.getLogger(DatabaseManager.class);
public DatabaseManager(Properties config) {
@@ -38,6 +46,14 @@ public abstract class DatabaseManager {
}
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) {
try {
logger.info("Closing database connection...");
@@ -50,6 +66,7 @@ public abstract class DatabaseManager {
logger.debug("No database connection to close");
}
}
protected boolean testConnection() {
try (Statement stmt = connection.createStatement()) {
stmt.execute("SELECT 1");
@@ -59,4 +76,67 @@ public abstract class DatabaseManager {
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 String connectionUrl;
public SqliteDatabaseManager(Properties config) {
super(config);
@@ -45,12 +45,14 @@ public class SqliteDatabaseManager extends DatabaseManager {
this.connectionUrl = "jdbc:sqlite:" + dbFile.toAbsolutePath().toString();
logger.info("Database file path: {}", dbFile);
logger.info("Connection URL: {}", this.connectionUrl);
initializeHibernate();
this.connection = createConnection();
configurePerformancePragmas();
performSanityChecks();
ensureSchemaExists();
logger.info("SQLite database initialized successfully");
}
@Override
@@ -2,33 +2,66 @@ package com.mediamanager.service.delegate;
import com.google.protobuf.ByteString;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.service.delegate.handler.CloseHandler;
import com.mediamanager.service.delegate.handler.EchoHandler;
import com.mediamanager.service.delegate.handler.HeartbeatHandler;
import com.mediamanager.repository.ArtistRepository;
import com.mediamanager.repository.GenreRepository;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import jakarta.persistence.EntityManagerFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class DelegateActionManager {
private static final Logger logger = LogManager.getLogger(DelegateActionManager.class);
private final Map<String, ActionHandler> handlerRegistry;
private final ServiceLocator serviceLocator;
private final EntityManagerFactory entityManagerFactory;
public DelegateActionManager(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.serviceLocator = new ServiceLocator();
initializeServices();
public DelegateActionManager() {
logger.debug("DelegateActionManager created");
this.handlerRegistry = new HashMap<>();
registerHandlers();
autoRegisterHandlers();
}
private void registerHandlers() {
handlerRegistry.put("echo",new EchoHandler());
handlerRegistry.put("heartbeat",new HeartbeatHandler());
handlerRegistry.put("close", new CloseHandler());
private void initializeServices() {
logger.info("Initializing services...");
GenreRepository genreRepository = new GenreRepository(entityManagerFactory);
GenreService genreService = new GenreService(genreRepository);
serviceLocator.register(GenreService.class, genreService);
ArtistRepository artistRepository = new ArtistRepository(entityManagerFactory);
ArtistService artistService = new ArtistService(artistRepository);
serviceLocator.register(ArtistService.class, artistService);
serviceLocator.logRegisteredServices();
logger.info("Services initialized successfully");
}
public void start(){
logger.info("DelegateActionManager started");
}
@@ -37,6 +70,117 @@ public class DelegateActionManager {
logger.info("DelegateActionManager stopped");
}
@SuppressWarnings("unchecked")
private ActionHandler instantiateHandler(Class<?> clazz) throws Exception {
if(!ActionHandler.class.isAssignableFrom(clazz)){
throw new IllegalArgumentException(
clazz.getName() + " is annotated with @Action but does not implement ActionHandler");
}
logger.debug("Attempting to instantiate handler: {}", clazz.getSimpleName());
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
// Sort constructors by parameter count (descending) to prefer DI constructors
java.util.Arrays.sort(constructors, (c1, c2) ->
Integer.compare(c2.getParameterCount(), c1.getParameterCount()));
for (Constructor<?> constructor : constructors) {
Class<?>[] paramTypes = constructor.getParameterTypes();
Object[] params = new Object[paramTypes.length];
boolean allDependenciesResolved = true;
for (int i = 0; i < paramTypes.length; i++) {
Object service = serviceLocator.get(paramTypes[i]);
if (service == null) {
allDependenciesResolved = false;
logger.debug("Cannot resolve dependency {} for {}",
paramTypes[i].getSimpleName(),
clazz.getSimpleName());
break; // Para de tentar esse construtor
}
params[i] = service;
}
if (allDependenciesResolved) {
logger.debug("Using constructor with {} params for {}",
paramTypes.length, clazz.getSimpleName());
return (ActionHandler) constructor.newInstance(params);
}
}
throw new IllegalStateException(
String.format(
"Cannot instantiate handler %s. No suitable constructor found. " +
"Make sure all required services are registered in ServiceLocator.",
clazz.getName()
)
);
}
private void autoRegisterHandlers() {
logger.info("Starting auto-registration of handlers...");
Reflections reflections = new Reflections(
"com.mediamanager.service.delegate.handler",
Scanners.TypesAnnotated
);
Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(Action.class);
logger.info("Found {} handler classes with @Action annotation", annotatedClasses.size());
int successCount = 0;
int failureCount = 0;
for (Class<?> handlerClass : annotatedClasses) {
try {
Action actionAnnotation = handlerClass.getAnnotation(Action.class);
String actionName = actionAnnotation.value();
logger.debug("Processing handler: {} for action '{}'",
handlerClass.getSimpleName(),
actionName);
ActionHandler handler = instantiateHandler(handlerClass);
handlerRegistry.put(actionName, handler);
logger.info("✓ Registered handler: '{}' -> {}",
actionName,
handlerClass.getSimpleName());
successCount++;
} catch (Exception e) {
logger.error("✗ Failed to register handler: {}",
handlerClass.getName(),
e);
failureCount++;
}
}
logger.info("Auto-registration complete: {} successful, {} failed, {} total",
successCount,
failureCount,
successCount + failureCount);
if (failureCount > 0) {
logger.warn("Some handlers failed to register. Check logs above for details.");
}
}
public TransportProtocol.Response ProcessedRequest(TransportProtocol.Request request){
String requestId = request.getRequestId();
logger.info("Processing request: {}", requestId);
@@ -0,0 +1,45 @@
package com.mediamanager.service.delegate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
public class ServiceLocator {
private static final Logger logger = LogManager.getLogger(ServiceLocator.class);
private final Map<Class<?>, Object> services = new HashMap<>();
public <T> void register(Class<T> serviceClass, T serviceInstance) {
if (serviceInstance == null) {
throw new IllegalArgumentException("Service instance cannot be null");
}
services.put(serviceClass, serviceInstance);
logger.debug("Registered service: {} -> {}",
serviceClass.getSimpleName(),
serviceInstance.getClass().getSimpleName());
}
@SuppressWarnings("unchecked")
public <T> T get(Class<T> serviceClass) {
return (T) services.get(serviceClass);
}
public boolean has(Class<?> serviceClass) {
return services.containsKey(serviceClass);
}
public int size() {
return services.size();
}
public void logRegisteredServices() {
logger.info("Registered services: {}", services.size());
services.forEach((clazz, instance) ->
logger.info(" - {} -> {}",
clazz.getSimpleName(),
instance.getClass().getSimpleName())
);
}
}
@@ -0,0 +1,13 @@
package com.mediamanager.service.delegate.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Action {
String value();
}
@@ -6,9 +6,11 @@ import com.mediamanager.protocol.TestProtocol.CloseCommand;
import com.mediamanager.protocol.TestProtocol.CloseResponse;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("close")
public class CloseHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(CloseHandler.class);
@@ -6,9 +6,11 @@ import com.mediamanager.protocol.TestProtocol.EchoCommand; // ← Import
import com.mediamanager.protocol.TestProtocol.EchoResponse; // ← Import
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("echo")
public class EchoHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(EchoHandler.class);
@@ -6,9 +6,11 @@ import com.mediamanager.protocol.TestProtocol.HeartbeatCommand;
import com.mediamanager.protocol.TestProtocol.HeartbeatResponse;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("heartbeat")
public class HeartbeatHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(HeartbeatHandler.class);
@@ -0,0 +1,50 @@
package com.mediamanager.service.delegate.handler.artist;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mediamanager.mapper.ArtistMapper;
import com.mediamanager.model.Artist;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.protocol.messages.ArtistMessages;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("artist.create")
public class CreateArtistHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(CreateArtistHandler.class);
private final ArtistService artistService;
public CreateArtistHandler(ArtistService artistService) {
this.artistService = artistService;
}
@Override
public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException {
try{
ArtistMessages.CreateArtistRequest createRequest =
ArtistMessages.CreateArtistRequest.parseFrom(requestPayload);
Artist artist = artistService.createArtist(createRequest.getName());
ArtistMessages.Artist artistProto = ArtistMapper.toProtobuf(artist);
ArtistMessages.CreateArtistResponse createArtistResponse = ArtistMessages.CreateArtistResponse.newBuilder()
.setArtist(artistProto)
.build();
return TransportProtocol.Response.newBuilder()
.setPayload(createArtistResponse.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 artist", e);
return TransportProtocol.Response.newBuilder()
.setStatusCode(500)
.setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage()));
}
}
}
@@ -0,0 +1,62 @@
package com.mediamanager.service.delegate.handler.artist;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.protocol.messages.ArtistMessages;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("artist.delete")
public class DeleteArtistHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(DeleteArtistHandler.class);
private final ArtistService artistService;
public DeleteArtistHandler(ArtistService artistService) {
this.artistService = artistService;
}
@Override
public TransportProtocol.Response.Builder handle(ByteString requestPayload)
throws InvalidProtocolBufferException {
try {
ArtistMessages.DeleteArtistRequest deleteRequest =
ArtistMessages.DeleteArtistRequest.parseFrom(requestPayload);
int id = deleteRequest.getId();
boolean success = artistService.deleteArtist(id);
ArtistMessages.DeleteArtistResponse deleteResponse;
if (success) {
deleteResponse = ArtistMessages.DeleteArtistResponse.newBuilder()
.setSuccess(true)
.setMessage("Artist deleted successfully")
.build();
return TransportProtocol.Response.newBuilder()
.setPayload(deleteResponse.toByteString());
} else {
deleteResponse = ArtistMessages.DeleteArtistResponse.newBuilder()
.setSuccess(false)
.setMessage("Artist not found")
.build();
return TransportProtocol.Response.newBuilder()
.setStatusCode(404)
.setPayload(deleteResponse.toByteString());
}
} catch (Exception e) {
logger.error("Error deleting artist", e);
ArtistMessages.DeleteArtistResponse deleteResponse =
ArtistMessages.DeleteArtistResponse.newBuilder()
.setSuccess(false)
.setMessage("Error: " + e.getMessage())
.build();
return TransportProtocol.Response.newBuilder()
.setStatusCode(500)
.setPayload(deleteResponse.toByteString());
}
}
}
@@ -0,0 +1,56 @@
package com.mediamanager.service.delegate.handler.artist;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mediamanager.mapper.ArtistMapper;
import com.mediamanager.model.Artist;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.protocol.messages.ArtistMessages;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Optional;
@Action(value = "artist.getById")
public class GetArtistByIdHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(GetArtistByIdHandler.class);
private final ArtistService artistService;
public GetArtistByIdHandler(ArtistService artistService) {
this.artistService = artistService;
}
@Override
public TransportProtocol.Response.Builder handle(ByteString requestPayload)
throws InvalidProtocolBufferException{
try{
ArtistMessages.GetArtistByIdRequest getByIdRequest =
ArtistMessages.GetArtistByIdRequest.parseFrom(requestPayload);
int id = getByIdRequest.getId();
Optional<Artist> artistOpt = artistService.getArtistById(id);
if (artistOpt.isEmpty()){
logger.warn("Artist not found with ID: {}", id);
return TransportProtocol.Response.newBuilder()
.setStatusCode(404)
.setPayload(ByteString.copyFromUtf8("Artist not found"));
}
ArtistMessages.Artist artistProto = ArtistMapper.toProtobuf(artistOpt.get());
ArtistMessages.GetArtistByIdResponse getByIdResponse = ArtistMessages.GetArtistByIdResponse.newBuilder()
.setArtist(artistProto)
.build();
return TransportProtocol.Response.newBuilder()
.setPayload(getByIdResponse.toByteString());
} catch (Exception e) {
logger.error("Error getting artist by ID", e);
return TransportProtocol.Response.newBuilder()
.setStatusCode(500)
.setPayload(ByteString.copyFromUtf8("Error: "+ e.getMessage()));
}
}
}
@@ -0,0 +1,48 @@
package com.mediamanager.service.delegate.handler.artist;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mediamanager.mapper.ArtistMapper;
import com.mediamanager.model.Artist;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.protocol.messages.ArtistMessages;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
@Action("artist.getAll")
public class GetArtistHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(GetArtistHandler.class);
private final ArtistService artistService;
public GetArtistHandler(ArtistService artistService){this.artistService = artistService;}
@Override
public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException {
try{
List<Artist> artists = artistService.getAllArtists();
ArtistMessages.GetArtistsResponse.Builder responseBuilder = ArtistMessages.GetArtistsResponse.newBuilder();
for (Artist artist : artists) {
ArtistMessages.Artist artistProto = ArtistMapper.toProtobuf(artist);
responseBuilder.addArtists(artistProto);
}
ArtistMessages.GetArtistsResponse getArtistsResponse = responseBuilder.build();
return TransportProtocol.Response.newBuilder()
.setPayload(getArtistsResponse.toByteString());
}catch (Exception e){
logger.error("Error getting artists", e);
return TransportProtocol.Response.newBuilder()
.setStatusCode(500)
.setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage()));
}
}
}
@@ -0,0 +1,65 @@
package com.mediamanager.service.delegate.handler.artist;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mediamanager.mapper.ArtistMapper;
import com.mediamanager.model.Artist;
import com.mediamanager.protocol.TransportProtocol;
import com.mediamanager.protocol.messages.ArtistMessages;
import com.mediamanager.service.artist.ArtistService;
import com.mediamanager.service.delegate.ActionHandler;
import com.mediamanager.service.delegate.annotation.Action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Optional;
@Action("artist.update")
public class UpdateArtistHandler implements ActionHandler {
private static final Logger logger = LogManager.getLogger(UpdateArtistHandler.class);
private final ArtistService artistService;
public UpdateArtistHandler(ArtistService artistService) {
this.artistService = artistService;
}
@Override
public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException {
try{
ArtistMessages.UpdateArtistRequest updateRequest =
ArtistMessages.UpdateArtistRequest.parseFrom(requestPayload);
int id = updateRequest.getId();
String newName = updateRequest.getName();
Optional<Artist> artistOpt = artistService.updateArtist(id, newName);
if(artistOpt.isEmpty()){
logger.warn("Artist not found with ID: {}", id);
return TransportProtocol.Response.newBuilder()
.setStatusCode(404)
.setPayload(ByteString.copyFromUtf8("Artist not found"));
}
ArtistMessages.Artist artistProto = ArtistMapper.toProtobuf(artistOpt.get());
ArtistMessages.UpdateArtistResponse updateResponse = ArtistMessages.UpdateArtistResponse.newBuilder()
.setArtist(artistProto)
.build();
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 artist", e);
return TransportProtocol.Response.newBuilder()
.setStatusCode(500)
.setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage()));
}
}
}
@@ -0,0 +1,62 @@
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.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("genre.create")
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,72 @@
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.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Action("genre.delete")
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,66 @@
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.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Optional;
@Action("genre.getById")
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,56 @@
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.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
@Action("genre.getAll")
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,73 @@
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.delegate.annotation.Action;
import com.mediamanager.service.genre.GenreService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Optional;
@Action("genre.update")
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);
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 = "ArtistMessages";
package mediamanager.messages;
message Artist {
int32 id = 1;
string name = 2;
}
message CreateArtistRequest {
string name = 1;
}
message CreateArtistResponse {
Artist artist = 1;
}
message GetArtistsRequest {
}
message GetArtistsResponse {
repeated Artist artists = 1;
}
message GetArtistByIdRequest {
int32 id = 1;
}
message GetArtistByIdResponse {
Artist artist = 1;
}
message UpdateArtistRequest {
int32 id = 1;
string name = 2; // Novo nome
}
message UpdateArtistResponse {
Artist Artist = 1;
}
message DeleteArtistRequest {
int32 id = 1;
}
message DeleteArtistResponse {
bool success = 1;
string message = 2;
}
+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;
}