diff --git a/.gitignore b/.gitignore index 5074d96..30116b9 100644 --- a/.gitignore +++ b/.gitignore @@ -39,8 +39,16 @@ build/ .DS_Store ### Application Specific ### -application-local.properties +config-local.properties *.db pipes/ *.log /.idea/ + + Configuration files with sensitive credentials +# These files contain database passwords and API keys and should never be committed +src/main/resources/config.properties +src/main/resources/application.properties + +# Allow example configuration files to be committed +!src/main/resources/*.properties.example \ No newline at end of file diff --git a/README.md b/README.md index baf75eb..56207a1 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ MediaManager-Core/ │ │ ├── util/ # Utility classes │ │ └── MediaManagerApplication.java │ └── resources/ -│ ├── application.properties # App configuration +│ ├── config.properties # App configuration │ ├── log4j2.xml # Logging configuration │ └── META-INF/ │ └── persistence.xml # JPA configuration @@ -55,7 +55,7 @@ GRANT ALL PRIVILEGES ON DATABASE mediamanager TO mediamanager; ### 2. Configuration -Edit `src/main/resources/application.properties` and update the database credentials: +Edit `src/main/resources/config.properties` and update the database credentials: ```properties db.url=jdbc:postgresql://localhost:5432/mediamanager @@ -90,7 +90,7 @@ The application creates named pipes for inter-process communication. Default con - Pipe name: `mediamanager-pipe` - Buffer size: 8192 bytes -You can modify these settings in `application.properties`. +You can modify these settings in `config.properties`. ## Logging @@ -116,8 +116,8 @@ mvn test ## Dependencies -- PostgreSQL Driver: 42.7.3 -- Hibernate ORM: 6.4.4.Final +- PostgreSQL Driver: 42.7.5 +- Hibernate ORM: 7.1.7.Final - HikariCP: 5.1.0 - Log4j 2: 2.23.1 - Jackson: 2.16.1 diff --git a/pom.xml b/pom.xml index 9a87f4d..8846994 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ 17 UTF-8 7.1.7.Final - 42.7.5 + 42.7.7 5.1.0 2.23.1 5.10.2 @@ -35,6 +35,12 @@ ${hibernate.version} + + org.hibernate.orm + hibernate-community-dialects + ${hibernate.version} + + com.zaxxer @@ -55,7 +61,7 @@ org.apache.logging.log4j - log4j-slf4j2-impl + log4j-slf4j-impl ${log4j.version} @@ -78,9 +84,32 @@ ${junit.version} test + + com.google.protobuf + protobuf-java + 4.32.0 + + + org.xerial + sqlite-jdbc + 3.44.1.0 + + + org.reflections + reflections + 0.10.2 + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + + org.apache.maven.plugins @@ -91,11 +120,75 @@ 17 + org.apache.maven.plugins maven-surefire-plugin 3.2.5 + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + /usr/bin/protoc + ${project.basedir}/src/main/proto + ${project.build.directory}/generated-sources/protobuf/java + false + + + + + compile + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/protobuf/java + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + + shade + + + + + com.mediamanager.MediaManagerApplication + + + + + + diff --git a/src/main/java/com/mediamanager/MediaManagerApplication.java b/src/main/java/com/mediamanager/MediaManagerApplication.java index d3030da..22834fb 100644 --- a/src/main/java/com/mediamanager/MediaManagerApplication.java +++ b/src/main/java/com/mediamanager/MediaManagerApplication.java @@ -1,5 +1,8 @@ package com.mediamanager; +import com.mediamanager.service.database.DatabaseManager; +import com.mediamanager.service.delegate.DelegateActionManager; +import com.mediamanager.service.ipc.IPCManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -7,9 +10,25 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; +import com.mediamanager.service.database.SqliteDatabaseManager; + public class MediaManagerApplication { private static final Logger logger = LogManager.getLogger(MediaManagerApplication.class); private static Properties config; + private static DatabaseManager databaseManager; + private static DelegateActionManager actionManager; + private static IPCManager ipcManager; + + public enum ApplicationMode { + LOCAL("local"), + SERVER("server"); + + private final String value; + + ApplicationMode(String value) { + this.value = value; + } + } public static void main(String[] args) { logger.info("Starting MediaManager Core Application..."); @@ -17,19 +36,85 @@ public class MediaManagerApplication { try { // Load configuration loadConfiguration(); + String runTypeString = config.getProperty("runtype","local"); + ApplicationMode mode = null; + + for (ApplicationMode am : ApplicationMode.values()) { + if (am.value.equalsIgnoreCase(runTypeString)) { + mode = am; + break; + } + } + if (mode == null) { + logger.error("Invalid run type: {}", runTypeString); + throw new Exception("Invalid run type: " + runTypeString); + } + logger.info("Run type: {}", mode); + switch (mode) { + case LOCAL: + logger.info("Starting local database..."); + databaseManager = new SqliteDatabaseManager(config); + break; + case SERVER: + throw new Exception("Server mode not yet implemented"); + default: + } + databaseManager.init(); + actionManager = new DelegateActionManager(databaseManager.getEntityManagerFactory()); + actionManager.start(); + ipcManager = new IPCManager(config,actionManager); + ipcManager.init(); - // TODO: Initialize database connection - // TODO: Initialize IPC server with named pipes // TODO: Start application services logger.info("MediaManager Core started successfully"); - logger.info("IPC Pipe: {}", config.getProperty("ipc.pipe.path") + "/" + config.getProperty("ipc.pipe.name")); + logger.info("IPC Socket: {}", ipcManager.getSocketPath().toAbsolutePath().toString()); // Keep application running Runtime.getRuntime().addShutdownHook(new Thread(() -> { + logger.info("Shutting down MediaManager Core..."); - // TODO: Cleanup resources + + + if (databaseManager != null) { + databaseManager.close(); + } + + if (ipcManager != null) { + try { + ipcManager.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + if (actionManager != null) { + actionManager.stop(); + } + + logger.info("MediaManager Core shutdown successfully"); + logger.info("Goodbye!"); + + + // Give Log4j2 time to write all pending messages before shutting down + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + // Now shutdown Log4j2 + org.apache.logging.log4j.LogManager.shutdown(); })); + logger.info("Application is running"); + logger.info("Press Ctrl+C to exit"); + Thread.currentThread().join(); + + } catch (InterruptedException e) { + + logger.info("Application interrupted, initiating shutdown..."); + + Thread.currentThread().interrupt(); } catch (Exception e) { logger.error("Failed to start MediaManager Core", e); @@ -39,10 +124,9 @@ public class MediaManagerApplication { private static void loadConfiguration() throws IOException { config = new Properties(); - try (InputStream input = MediaManagerApplication.class.getClassLoader() - .getResourceAsStream("application.properties")) { + try (InputStream input = MediaManagerApplication.class.getClassLoader().getResourceAsStream("config.properties")) { if (input == null) { - throw new IOException("Unable to find application.properties"); + throw new IOException("Unable to find config.properties"); } config.load(input); logger.info("Configuration loaded successfully"); @@ -52,4 +136,4 @@ public class MediaManagerApplication { public static Properties getConfig() { return config; } -} +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/mapper/ArtistMapper.java b/src/main/java/com/mediamanager/mapper/ArtistMapper.java new file mode 100644 index 0000000..29f2ce9 --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/ArtistMapper.java @@ -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; + } +} diff --git a/src/main/java/com/mediamanager/mapper/BitDepthMapper.java b/src/main/java/com/mediamanager/mapper/BitDepthMapper.java new file mode 100644 index 0000000..873629c --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/BitDepthMapper.java @@ -0,0 +1,39 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.BitDepth; +import com.mediamanager.protocol.messages.BitDepthMessages; + +public class BitDepthMapper { + public static BitDepthMessages.BitDepth toProtobuf(BitDepth entity) { + if (entity == null){ + return null; + } + + String value = entity.getValue(); + if (value == null) { + throw new IllegalArgumentException("Bit depth value cannot be null"); + } + BitDepthMessages.BitDepth.Builder builder = BitDepthMessages.BitDepth.newBuilder() + .setValue(value); + + Integer id = entity.getId(); + if (id != null && id > 0) { + builder.setId(id); + } + + return builder.build(); + } + + public static BitDepth toEntity(BitDepthMessages.BitDepth protobuf) { + if (protobuf == null) { + return null; + } + BitDepth entity = new BitDepth(); + + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + entity.setValue(protobuf.getValue()); + return entity; + } +} diff --git a/src/main/java/com/mediamanager/mapper/ComposerMapper.java b/src/main/java/com/mediamanager/mapper/ComposerMapper.java new file mode 100644 index 0000000..cf783b5 --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/ComposerMapper.java @@ -0,0 +1,38 @@ +package com.mediamanager.mapper; + +import com.mediamanager.model.Composer; +import com.mediamanager.protocol.messages.ComposerMessages; + +public class ComposerMapper { + public static ComposerMessages.Composer toProtobuf(Composer entity) { + if (entity == null) { + return null; + } + ComposerMessages.Composer.Builder builder = ComposerMessages.Composer.newBuilder() + .setName(entity.getName()); + + Integer id = entity.getId(); + if (id != null && id > 0) { + builder.setId(id); + } + + return builder.build(); + + } + + public static Composer toEntity(ComposerMessages.Composer protobuf) { + if (protobuf == null) { + return null; + } + + Composer entity = new Composer(); + + if (protobuf.getId() > 0) { + entity.setId(protobuf.getId()); + } + + entity.setName(protobuf.getName()); + + return entity; + } +} 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..331e67c --- /dev/null +++ b/src/main/java/com/mediamanager/mapper/GenreMapper.java @@ -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; + } +} + diff --git a/src/main/java/com/mediamanager/model/Artist.java b/src/main/java/com/mediamanager/model/Artist.java new file mode 100644 index 0000000..22fe490 --- /dev/null +++ b/src/main/java/com/mediamanager/model/Artist.java @@ -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; + } +} diff --git a/src/main/java/com/mediamanager/model/BitDepth.java b/src/main/java/com/mediamanager/model/BitDepth.java new file mode 100644 index 0000000..3b80217 --- /dev/null +++ b/src/main/java/com/mediamanager/model/BitDepth.java @@ -0,0 +1,32 @@ +package com.mediamanager.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "bit_depth") +public class BitDepth { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(nullable = false) + private String value; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} + diff --git a/src/main/java/com/mediamanager/model/Composer.java b/src/main/java/com/mediamanager/model/Composer.java new file mode 100644 index 0000000..965bf6f --- /dev/null +++ b/src/main/java/com/mediamanager/model/Composer.java @@ -0,0 +1,31 @@ +package com.mediamanager.model; + + +import jakarta.persistence.*; + +@Entity +@Table(name = "composer") +public class Composer { + @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/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/model/Media.java b/src/main/java/com/mediamanager/model/Media.java deleted file mode 100644 index 99a455d..0000000 --- a/src/main/java/com/mediamanager/model/Media.java +++ /dev/null @@ -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 - } -} diff --git a/src/main/java/com/mediamanager/repository/ArtistRepository.java b/src/main/java/com/mediamanager/repository/ArtistRepository.java new file mode 100644 index 0000000..60d0b9d --- /dev/null +++ b/src/main/java/com/mediamanager/repository/ArtistRepository.java @@ -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 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 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(); + } + } + } + + diff --git a/src/main/java/com/mediamanager/repository/BitDepthRepository.java b/src/main/java/com/mediamanager/repository/BitDepthRepository.java new file mode 100644 index 0000000..24b9dc9 --- /dev/null +++ b/src/main/java/com/mediamanager/repository/BitDepthRepository.java @@ -0,0 +1,100 @@ +package com.mediamanager.repository; + +import com.mediamanager.model.BitDepth; +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 BitDepthRepository { + private static final Logger logger = LogManager.getLogger(BitDepthRepository.class); + + private final EntityManagerFactory entityManagerFactory; + + public BitDepthRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public BitDepth save(BitDepth bitDepth) { + logger.debug("Saving BitDepth: {}", bitDepth.getValue()); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try { + em.persist(bitDepth); + em.getTransaction().commit(); + logger.debug("BitDepth saved with ID: {}", bitDepth.getId()); + return bitDepth; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error saving BitDepth", e); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + + public List findAll(){ + logger.debug("Finding all BitDepths"); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + return em.createQuery("SELECT b FROM BitDepth b ORDER BY b.value", BitDepth.class).getResultList(); + }finally { + if (em.isOpen()) em.close(); + } + } + + public Optional findById(Integer id){ + logger.debug("Finding BitDepth by ID: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + BitDepth bitDepth = em.find(BitDepth.class, id); + return Optional.ofNullable(bitDepth); + }finally { + if (em.isOpen()) em.close(); + } + } + + public BitDepth update(BitDepth bitDepth){ + logger.debug("Updating BitDepth ID: {}", bitDepth.getId()); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + BitDepth updated = em.merge(bitDepth); + em.getTransaction().commit(); + logger.debug("BitDepth updated successfully"); + return updated; + }catch (Exception e){ + em.getTransaction().rollback(); + logger.error("Error updating BitDepth", e); + throw e; + }finally { + if (em.isOpen()) em.close(); + } + } + + public boolean deleteById(Integer id){ + logger.debug("Deleting BitDepth by ID: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + BitDepth bitDepth = em.find(BitDepth.class, id); + if (bitDepth == null) { + em.getTransaction().rollback(); + return false; + } + em.remove(bitDepth); + em.getTransaction().commit(); + logger.debug("BitDepth deleted successfully"); + return true; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error deleting BitDepth", e); + throw e; + }finally { + if (em.isOpen()) em.close(); + } + } +} diff --git a/src/main/java/com/mediamanager/repository/ComposerRepository.java b/src/main/java/com/mediamanager/repository/ComposerRepository.java new file mode 100644 index 0000000..6e1a2aa --- /dev/null +++ b/src/main/java/com/mediamanager/repository/ComposerRepository.java @@ -0,0 +1,102 @@ +package com.mediamanager.repository; + +import com.mediamanager.model.Composer; +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 ComposerRepository { + private static final Logger logger = LogManager.getLogger(ComposerRepository.class); + + private final EntityManagerFactory entityManagerFactory; + + public ComposerRepository(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public Composer save(Composer composer) { + logger.debug("Saving composer: {}", composer.getName()); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + //ToDo: Add Id Validation + //ToDo: Add to all Repositories + em.persist(composer); + em.getTransaction().commit(); + logger.debug("Composer saved with IS: {}", composer.getId()); + return composer; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error saving composer", e); + throw e; + }finally { + if (em.isOpen()) em.close(); + } + } + + public List findAll(){ + logger.debug("Finding all composers"); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + return em.createQuery("SELECT c FROM Composer c ORDER BY c.name", Composer.class).getResultList(); + } finally { + if (em.isOpen()) em.close(); + } + } + + public Optional findById(Integer id){ + logger.debug("Finding composer by ID: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + try{ + Composer composer = em.find(Composer.class, id); + return Optional.ofNullable(composer); + } finally { + if (em.isOpen()) em.close(); + } + } + + public Composer update(Composer composer){ + logger.debug("Updating composer ID: {}", composer.getId()); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + Composer updated = em.merge(composer); + em.getTransaction().commit(); + logger.debug("Composer updated successfully"); + return updated; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error updating composer", e); + throw e; + } finally { + if (em.isOpen()) em.close(); + } + } + + public boolean deleteById(Integer id){ + logger.debug("Deleting composer by ID: {}", id); + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + try{ + Composer composer = em.find(Composer.class, id); + if (composer == null) { + em.getTransaction().rollback(); + return false; + } + em.remove(composer); + em.getTransaction().commit(); + logger.debug("Composer deleted successfully"); + return true; + } catch (Exception e) { + em.getTransaction().rollback(); + logger.error("Error deleting composer", e); + throw e; + }finally { + if (em.isOpen()) em.close(); + } + } +} 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..73e9be9 --- /dev/null +++ b/src/main/java/com/mediamanager/repository/GenreRepository.java @@ -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 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 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(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/artist/ArtistService.java b/src/main/java/com/mediamanager/service/artist/ArtistService.java new file mode 100644 index 0000000..4686030 --- /dev/null +++ b/src/main/java/com/mediamanager/service/artist/ArtistService.java @@ -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 getAllArtists(){ + logger.info("Getting all artists"); + return artistRepository.findAll(); + } + + public Optional getArtistById(Integer id){ + logger.info("Getting artist by ID: {}", id); + return artistRepository.findById(id); + } + + public Optional 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 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); + } + + + +} diff --git a/src/main/java/com/mediamanager/service/bitdepth/BitDepthService.java b/src/main/java/com/mediamanager/service/bitdepth/BitDepthService.java new file mode 100644 index 0000000..495e5d0 --- /dev/null +++ b/src/main/java/com/mediamanager/service/bitdepth/BitDepthService.java @@ -0,0 +1,61 @@ +package com.mediamanager.service.bitdepth; + +import com.mediamanager.model.BitDepth; +import com.mediamanager.repository.BitDepthRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +public class BitDepthService { + private static final Logger logger = LogManager.getLogger(BitDepthService.class); + private final BitDepthRepository bitDepthRepository; + + public BitDepthService(BitDepthRepository bitDepthRepository) { + this.bitDepthRepository = bitDepthRepository; + } + public BitDepth createBitDepth(String value){ + logger.info("Creating bit-depth: {}", value); + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Bit-depth value cannot be empty"); + } + BitDepth bitDepth = new BitDepth(); + bitDepth.setValue(value.trim()); + return bitDepthRepository.save(bitDepth); + + } + + public List getAllBitDepths(){ + logger.info("Getting all bit-depths"); + return bitDepthRepository.findAll(); + } + + public Optional getBitDepthById(Integer id){ + logger.info("Getting bit-depth by ID: {}", id); + return bitDepthRepository.findById(id); + } + + public Optional updateBitDepth(Integer id, String value){ + logger.info("Updating bit-depth ID {}: {}", id, value); + + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("Bit-depth value cannot be empty"); + } + Optional existingBitDepth = bitDepthRepository.findById(id); + if(existingBitDepth.isEmpty()){ + logger.warn("Bit-depth not found with ID: {}", id); + return Optional.empty(); + } + BitDepth bitDepth = existingBitDepth.get(); + bitDepth.setValue(value.trim()); + BitDepth updatedBitDepth = bitDepthRepository.update(bitDepth); + return Optional.of(updatedBitDepth); + + } + + public boolean deleteBitDepth(Integer id){ + logger.info("Deleting bit-depth ID: {}", id); + return bitDepthRepository.deleteById(id); + } +} diff --git a/src/main/java/com/mediamanager/service/composer/ComposerService.java b/src/main/java/com/mediamanager/service/composer/ComposerService.java new file mode 100644 index 0000000..4f2e5e7 --- /dev/null +++ b/src/main/java/com/mediamanager/service/composer/ComposerService.java @@ -0,0 +1,70 @@ +package com.mediamanager.service.composer; + +import com.mediamanager.model.Composer; +import com.mediamanager.repository.ComposerRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Optional; + +public class ComposerService { + private static final Logger logger = LogManager.getLogger(ComposerService.class); + + private final ComposerRepository composerRepository; + + public ComposerService(ComposerRepository composerRepository) { + this.composerRepository = composerRepository; + } + + public Composer createComposer(String name) { + logger.info("Creating composer: {}", name); + if(name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Composer name cannot be empty"); + } + Composer composer = new Composer(); + composer.setName(name.trim()); + return composerRepository.save(composer); + + + } + + public List getAllComposers() { + logger.info("Getting all composers"); + return composerRepository.findAll(); + } + + public Optional getComposerById(Integer id) { + logger.info("Getting composer by ID: {}", id); + return composerRepository.findById(id); + } + + public Optional updateComposer(Integer id, String name) { + logger.info("Updating composer ID {}: {}", id, name); + + if(name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Composer name cannot be empty"); + } + + Optional existingComposer = composerRepository.findById(id); + + if(existingComposer.isEmpty()) { + logger.warn("Composer not found with ID: {}", id); + return Optional.empty(); + } + + Composer composer = existingComposer.get(); + composer.setName(name.trim()); + + Composer updated = composerRepository.update(composer); + return Optional.of(updated); + + } + + public boolean deleteComposer(Integer id) { + logger.info("Deleting composer ID: {}", id); + return composerRepository.deleteById(id); + } + + +} diff --git a/src/main/java/com/mediamanager/service/database/DatabaseManager.java b/src/main/java/com/mediamanager/service/database/DatabaseManager.java new file mode 100644 index 0000000..13188a0 --- /dev/null +++ b/src/main/java/com/mediamanager/service/database/DatabaseManager.java @@ -0,0 +1,142 @@ +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) { + this.configuration = config; + logger.debug("DatabaseManager created with configuration:"); + } + + public abstract void init() throws Exception; + + protected abstract Connection createConnection() throws Exception; + + protected void performSanityChecks() throws SQLException { + logger.debug("Performing sanity checks..."); + try (Statement stmt = connection.createStatement()) { + stmt.execute("SELECT 1"); + + } + String databaseProductName = connection.getMetaData().getDatabaseProductName(); + String databaseProductVersion = connection.getMetaData().getDatabaseProductVersion(); + logger.info("Connected to database: {} v{}", databaseProductName, databaseProductVersion); + } + + public Connection getConnection() { + return connection; + } + + 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..."); + connection.close(); + logger.info("Database connection closed successfully"); + } catch (SQLException e) { + logger.error("Error closing database connection: {}", e.getMessage()); + } + } else { + logger.debug("No database connection to close"); + } + } + + protected boolean testConnection() { + try (Statement stmt = connection.createStatement()) { + stmt.execute("SELECT 1"); + return true; + } catch (SQLException e) { + logger.error("Connection test failed", e); + 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> 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/database/SqliteDatabaseManager.java b/src/main/java/com/mediamanager/service/database/SqliteDatabaseManager.java new file mode 100644 index 0000000..dc98c91 --- /dev/null +++ b/src/main/java/com/mediamanager/service/database/SqliteDatabaseManager.java @@ -0,0 +1,99 @@ +package com.mediamanager.service.database; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +public class SqliteDatabaseManager extends DatabaseManager { + private static final Logger logger = LogManager.getLogger(SqliteDatabaseManager.class); + + + + + public SqliteDatabaseManager(Properties config) { + super(config); + } + + @Override + public void init() throws Exception { + + logger.info("Initializing SQLite database..."); + String dataDir = configuration.getProperty("database.dir", + System.getProperty("user.home") + "/.mediamanager/db"); + String dbFilename = configuration.getProperty("database.filename", "mediamanager.db"); + String driverClassName = configuration.getProperty("database.driver", "org.sqlite.JDBC"); + try { + Class.forName(driverClassName); + }catch(ClassNotFoundException e) { + logger.error("Failed to load SQLite driver", e); + throw e; + } + Path dataPath = Paths.get(dataDir); + if (!Files.exists(dataPath)) { + Files.createDirectories(dataPath); + logger.debug("Created database directory: {}", dataDir); + } + Path dbFile = dataPath.resolve(dbFilename); + 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 + protected Connection createConnection() throws Exception { + try { + // O driver org.xerial.sqlite-jdbc é carregado automaticamente aqui + Connection conn = DriverManager.getConnection(this.connectionUrl); + logger.debug("Got connection to SQLite file"); + return conn; + } catch (SQLException e) { + logger.error("Failed to create SQLite connection", e); + throw new Exception("SQLite connection failed: " + e.getMessage(), e); + } + } + + + private void configurePerformancePragmas() throws SQLException { + try (Statement stmt = connection.createStatement()) { + + stmt.execute("PRAGMA journal_mode=WAL;"); + + + stmt.execute("PRAGMA foreign_keys=ON;"); + + + stmt.execute("PRAGMA synchronous=NORMAL;"); + + stmt.execute("PRAGMA busy_timeout=5000;"); + + logger.debug("SQLite performance PRAGMAs applied (WAL, Synchronous, ForeignKeys)."); + } + } + + private void ensureSchemaExists() throws SQLException { + + } + + @Override + public void close() { + + super.close(); + logger.info("SQLite resources released."); + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/ActionHandler.java b/src/main/java/com/mediamanager/service/delegate/ActionHandler.java new file mode 100644 index 0000000..27228fc --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/ActionHandler.java @@ -0,0 +1,11 @@ +package com.mediamanager.service.delegate; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; + +@FunctionalInterface +public interface ActionHandler { + TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException; +} + diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java new file mode 100644 index 0000000..338d6a7 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -0,0 +1,218 @@ +package com.mediamanager.service.delegate; + +import com.google.protobuf.ByteString; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.repository.*; +import com.mediamanager.service.bitdepth.BitDepthService; +import com.mediamanager.service.composer.ComposerService; +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 handlerRegistry; + private final ServiceLocator serviceLocator; + private final EntityManagerFactory entityManagerFactory; + + + public DelegateActionManager(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + this.serviceLocator = new ServiceLocator(); + + initializeServices(); + + + logger.debug("DelegateActionManager created"); + this.handlerRegistry = new HashMap<>(); + autoRegisterHandlers(); + + } + + 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); + + ComposerRepository composerRepository = new ComposerRepository(entityManagerFactory); + ComposerService composerService = new ComposerService(composerRepository); + serviceLocator.register(ComposerService.class, composerService); + + BitDepthRepository bitDepthRepository = new BitDepthRepository(entityManagerFactory); + BitDepthService bitDepthService = new BitDepthService(bitDepthRepository); + serviceLocator.register(BitDepthService.class, bitDepthService); + + serviceLocator.logRegisteredServices(); + + logger.info("Services initialized successfully"); + } + + + + public void start(){ + logger.info("DelegateActionManager started"); + } + + public void stop(){ + 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> 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); + String action = request.getHeadersMap().getOrDefault("action", "unknown"); + ActionHandler handler = handlerRegistry.get(action); + TransportProtocol.Response.Builder responseBuilder; + if (handler == null) { + logger.warn("No handler found for action: {}", action); + responseBuilder = TransportProtocol.Response.newBuilder() + .setStatusCode(404) // 404 Not Found + .setPayload(ByteString.copyFromUtf8("Error: Action '" + action + "' not found.")); + } else{ + try { + logger.debug("Delegating action '{}' to handler...", action); + responseBuilder = handler.handle(request.getPayload()); + }catch (Exception e) { + logger.error("Handler for action '{}' threw an exception:", action, e); + responseBuilder = TransportProtocol.Response.newBuilder() + .setStatusCode(500) // 500 Internal Server Error + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } + return responseBuilder.setRequestId(requestId).build(); + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/ServiceLocator.java b/src/main/java/com/mediamanager/service/delegate/ServiceLocator.java new file mode 100644 index 0000000..69886f4 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/ServiceLocator.java @@ -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, Object> services = new HashMap<>(); + + public void register(Class 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 get(Class 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()) + ); + } +} + diff --git a/src/main/java/com/mediamanager/service/delegate/annotation/Action.java b/src/main/java/com/mediamanager/service/delegate/annotation/Action.java new file mode 100644 index 0000000..cac0db7 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/annotation/Action.java @@ -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(); +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java new file mode 100644 index 0000000..9e9c658 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java @@ -0,0 +1,34 @@ +package com.mediamanager.service.delegate.handler; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +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); + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + CloseCommand.parseFrom(requestPayload); // Valida + + logger.info("Close command received - connection will close"); + + CloseResponse response = CloseResponse.newBuilder() + .setMessage("Connection closing. Goodbye!") + .build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(ByteString.copyFrom(response.toByteArray())) + .setStatusCode(200) + .putHeaders("Connection", "close"); // ← Marca para fechar + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java new file mode 100644 index 0000000..657c2e7 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java @@ -0,0 +1,41 @@ +package com.mediamanager.service.delegate.handler; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +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); + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { // ← Pode lançar exceção + + // 1. Parse Protobuf bytes → EchoCommand + EchoCommand command = EchoCommand.parseFrom(requestPayload); + + logger.debug("Echo received: {}", command.getMessage()); + + // 2. Cria EchoResponse (Protobuf) + EchoResponse echoResponse = EchoResponse.newBuilder() + .setMessage(command.getMessage()) + .setServerTimestamp(System.currentTimeMillis()) + .build(); + + // 3. Serializa EchoResponse → bytes + ByteString responsePayload = ByteString.copyFrom(echoResponse.toByteArray()); + + // 4. Retorna Response + return TransportProtocol.Response.newBuilder() + .setPayload(responsePayload) + .setStatusCode(200); + + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java new file mode 100644 index 0000000..d79270c --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java @@ -0,0 +1,38 @@ +package com.mediamanager.service.delegate.handler; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +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); + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + HeartbeatCommand command = HeartbeatCommand.parseFrom(requestPayload); + + long serverTime = System.currentTimeMillis(); + + logger.debug("Heartbeat received. Client T1={}, Server T2={}", + command.getClientTimestamp(), serverTime); + + HeartbeatResponse response = HeartbeatResponse.newBuilder() + .setClientTimestamp(command.getClientTimestamp()) // Echo T1 + .setServerTimestamp(serverTime) // T2 + .build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(ByteString.copyFrom(response.toByteArray())) + .setStatusCode(200); + + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/artist/CreateArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/artist/CreateArtistHandler.java new file mode 100644 index 0000000..268a4bd --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/artist/CreateArtistHandler.java @@ -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())); + } + } +} + diff --git a/src/main/java/com/mediamanager/service/delegate/handler/artist/DeleteArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/artist/DeleteArtistHandler.java new file mode 100644 index 0000000..a3d3c72 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/artist/DeleteArtistHandler.java @@ -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()); + } + } + } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistByIdHandler.java new file mode 100644 index 0000000..d98b48e --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistByIdHandler.java @@ -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 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())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistHandler.java new file mode 100644 index 0000000..bf91fff --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/artist/GetArtistHandler.java @@ -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 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())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/artist/UpdateArtistHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/artist/UpdateArtistHandler.java new file mode 100644 index 0000000..69c3cd6 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/artist/UpdateArtistHandler.java @@ -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 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())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/CreateBitDepthHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/CreateBitDepthHandler.java new file mode 100644 index 0000000..e055baf --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/CreateBitDepthHandler.java @@ -0,0 +1,52 @@ +package com.mediamanager.service.delegate.handler.bitdepth; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.BitDepthMapper; +import com.mediamanager.model.BitDepth; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ArtistMessages; +import com.mediamanager.protocol.messages.BitDepthMessages; +import com.mediamanager.service.bitdepth.BitDepthService; +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("bitdepth.create") +public class CreateBitDepthHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateBitDepthHandler.class); + private final BitDepthService bitDepthService; + + public CreateBitDepthHandler(BitDepthService bitDepthService) { + this.bitDepthService = bitDepthService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + BitDepthMessages.CreateBitDepthRequest createRequest = + BitDepthMessages.CreateBitDepthRequest.parseFrom(requestPayload); + BitDepth bitDepth = bitDepthService.createBitDepth(createRequest.getValue()); + BitDepthMessages.BitDepth BitDepthProto = BitDepthMapper.toProtobuf(bitDepth); + BitDepthMessages.CreateBitDepthResponse createBitDepthResponse = BitDepthMessages.CreateBitDepthResponse.newBuilder() + .setBitdepth(BitDepthProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(createBitDepthResponse.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 bit-depth", e); + return TransportProtocol.Response.newBuilder() + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} + diff --git a/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/DeleteBitDepthHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/DeleteBitDepthHandler.java new file mode 100644 index 0000000..b71a9a1 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/DeleteBitDepthHandler.java @@ -0,0 +1,62 @@ +package com.mediamanager.service.delegate.handler.bitdepth; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.BitDepthMessages; +import com.mediamanager.service.bitdepth.BitDepthService; +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("bitdepth.delete") +public class DeleteBitDepthHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteBitDepthHandler.class); + + private final BitDepthService bitDepthService; + + public DeleteBitDepthHandler(BitDepthService bitDepthService) { + this.bitDepthService = bitDepthService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + try { + BitDepthMessages.DeleteBitDepthRequest deleteRequest = + BitDepthMessages.DeleteBitDepthRequest.parseFrom(requestPayload); + int id = deleteRequest.getId(); + boolean success = bitDepthService.deleteBitDepth(id); + BitDepthMessages.DeleteBitDepthResponse deleteResponse; + if (success) { + deleteResponse = BitDepthMessages.DeleteBitDepthResponse.newBuilder() + .setSuccess(true) + .setMessage("Bit-Depth deleted successfully") + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + } else { + deleteResponse = BitDepthMessages.DeleteBitDepthResponse.newBuilder() + .setSuccess(false) + .setMessage("Bit-Depth not found") + .build(); + + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + } catch (Exception e) { + logger.error("Error deleting bit-depth", e); + BitDepthMessages.DeleteBitDepthResponse deleteResponse = + BitDepthMessages.DeleteBitDepthResponse.newBuilder() + .setSuccess(false) + .setMessage("Error: " + e.getMessage()) + .build(); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(deleteResponse.toByteString()); + } + } + } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthByIdHandler.java new file mode 100644 index 0000000..96e630d --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthByIdHandler.java @@ -0,0 +1,56 @@ +package com.mediamanager.service.delegate.handler.bitdepth; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.BitDepthMapper; +import com.mediamanager.model.BitDepth; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.BitDepthMessages; +import com.mediamanager.service.bitdepth.BitDepthService; +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 = "bitdepth.getById") +public class GetBitDepthByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetBitDepthByIdHandler.class); + private final BitDepthService bitDepthServicee; + + public GetBitDepthByIdHandler(BitDepthService bitDepthServicee) { + this.bitDepthServicee = bitDepthServicee; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException{ + + try{ + BitDepthMessages.GetBitDepthByIdRequest getByIdRequest = + BitDepthMessages.GetBitDepthByIdRequest.parseFrom(requestPayload); + int id = getByIdRequest.getId(); + + Optional bitDepthOpt = bitDepthServicee.getBitDepthById(id); + + if (bitDepthOpt.isEmpty()){ + logger.warn("BitDepth not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("BitDepth not found")); + } + BitDepthMessages.BitDepth bitDepthProto = BitDepthMapper.toProtobuf(bitDepthOpt.get()); + BitDepthMessages.GetBitDepthByIdResponse getByIdResponse = BitDepthMessages.GetBitDepthByIdResponse.newBuilder() + .setBitdepth(bitDepthProto) + .build(); + return TransportProtocol.Response.newBuilder() + .setPayload(getByIdResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting bit-depth by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: "+ e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthHandler.java new file mode 100644 index 0000000..71cf92f --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/GetBitDepthHandler.java @@ -0,0 +1,48 @@ +package com.mediamanager.service.delegate.handler.bitdepth; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.BitDepthMapper; +import com.mediamanager.model.BitDepth; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.BitDepthMessages; +import com.mediamanager.service.bitdepth.BitDepthService; +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("bitdepth.getAll") +public class GetBitDepthHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetBitDepthHandler.class); + + private final BitDepthService bitDepthService; + + public GetBitDepthHandler(BitDepthService bitDepthService){this.bitDepthService = bitDepthService;} + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + List bitDepths = bitDepthService.getAllBitDepths(); + BitDepthMessages.GetBitDepthsResponse.Builder responseBuilder = BitDepthMessages.GetBitDepthsResponse.newBuilder(); + + for (BitDepth bitDepth : bitDepths) { + BitDepthMessages.BitDepth bitDepthProto = BitDepthMapper.toProtobuf(bitDepth); + responseBuilder.addBitdepths(bitDepthProto); + } + BitDepthMessages.GetBitDepthsResponse getBitDepthsResponse = responseBuilder.build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(getBitDepthsResponse.toByteString()); + + }catch (Exception e){ + logger.error("Error getting bit-depths", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/UpdateBitDepthHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/UpdateBitDepthHandler.java new file mode 100644 index 0000000..c786b12 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/bitdepth/UpdateBitDepthHandler.java @@ -0,0 +1,65 @@ +package com.mediamanager.service.delegate.handler.bitdepth; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.BitDepthMapper; +import com.mediamanager.model.BitDepth; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.BitDepthMessages; +import com.mediamanager.service.bitdepth.BitDepthService; +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("bitdepth.update") +public class UpdateBitDepthHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(UpdateBitDepthHandler.class); + private final BitDepthService bitDepthService; + + public UpdateBitDepthHandler(BitDepthService bitDepthService) { + this.bitDepthService = bitDepthService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) throws InvalidProtocolBufferException { + try{ + BitDepthMessages.UpdateBitDepthRequest updateRequest = + BitDepthMessages.UpdateBitDepthRequest.parseFrom(requestPayload); + + int id = updateRequest.getId(); + String newValue = updateRequest.getValue(); + + Optional bitDepthOpt = bitDepthService.updateBitDepth(id, newValue); + + if(bitDepthOpt.isEmpty()){ + logger.warn("BitDepth not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("BitDepth not found")); + } + + BitDepthMessages.BitDepth bitDepthProto = BitDepthMapper.toProtobuf(bitDepthOpt.get()); + + BitDepthMessages.UpdateBitDepthResponse updateResponse = BitDepthMessages.UpdateBitDepthResponse.newBuilder() + .setBitdepth(bitDepthProto) + .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 bit-depth", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/composer/CreateComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/composer/CreateComposerHandler.java new file mode 100644 index 0000000..5ec5658 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/composer/CreateComposerHandler.java @@ -0,0 +1,51 @@ +package com.mediamanager.service.delegate.handler.composer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.ComposerMapper; +import com.mediamanager.model.Composer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ComposerMessages; +import com.mediamanager.service.composer.ComposerService; +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("composer.create") +public class CreateComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CreateComposerHandler.class); + private final ComposerService composerService; + + public CreateComposerHandler(ComposerService composerService) { + this.composerService = composerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try{ + ComposerMessages.CreateComposerRequest CreateRequest = ComposerMessages.CreateComposerRequest + .parseFrom(requestPayload); + Composer composer = composerService.createComposer(CreateRequest.getName()); + ComposerMessages.Composer composerProto = ComposerMapper.toProtobuf(composer); + ComposerMessages.CreateComposerResponse createResponse = ComposerMessages.CreateComposerResponse + .newBuilder().setComposer(composerProto).build(); + 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 composer", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/composer/DeleteComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/composer/DeleteComposerHandler.java new file mode 100644 index 0000000..b159427 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/composer/DeleteComposerHandler.java @@ -0,0 +1,53 @@ +package com.mediamanager.service.delegate.handler.composer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ComposerMessages; +import com.mediamanager.service.composer.ComposerService; +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( "composer.delete") +public class DeleteComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(DeleteComposerHandler.class); + private final ComposerService composerService; + + + public DeleteComposerHandler(ComposerService composerService){ + this.composerService = composerService; + } + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try { + ComposerMessages.DeleteComposerRequest deleteRequest = + ComposerMessages.DeleteComposerRequest.parseFrom(requestPayload); + int id = deleteRequest.getId(); + boolean success = composerService.deleteComposer(id); + ComposerMessages.DeleteComposerResponse deleteResponse; + if(success){ + deleteResponse = ComposerMessages.DeleteComposerResponse.newBuilder().setSuccess(true) + .setMessage("Composer deleted successfully").build(); + + + return TransportProtocol.Response.newBuilder() + .setPayload(deleteResponse.toByteString()); + }else { + deleteResponse = ComposerMessages.DeleteComposerResponse.newBuilder().setSuccess(false) + .setMessage("Composer not found").build(); + return TransportProtocol.Response.newBuilder().setStatusCode(404) + .setPayload(deleteResponse.toByteString()); + } + + } catch (Exception e) { + logger.error("Error deleting composer", e); + ComposerMessages.DeleteComposerResponse deleteResponse = ComposerMessages.DeleteComposerResponse + .newBuilder().setSuccess(false).setMessage("Error: " + e.getMessage()).build(); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500).setPayload(deleteResponse.toByteString()); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerByIdHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerByIdHandler.java new file mode 100644 index 0000000..d1336b9 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerByIdHandler.java @@ -0,0 +1,57 @@ +package com.mediamanager.service.delegate.handler.composer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.ComposerMapper; +import com.mediamanager.model.Composer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ComposerMessages; +import com.mediamanager.service.composer.ComposerService; +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( "composer.getById") +public class GetComposerByIdHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetComposerByIdHandler.class); + private final ComposerService composerService; + + public GetComposerByIdHandler(ComposerService composerService){ + this.composerService = composerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try{ + + ComposerMessages.GetComposerByIdRequest getByIdRequest = ComposerMessages.GetComposerByIdRequest + .parseFrom(requestPayload); + int id = getByIdRequest.getId(); + + Optional composerOpt = composerService.getComposerById(id); + + if (composerOpt.isEmpty()) { + logger.warn("Composer not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("Composer not found")); + } + + ComposerMessages.Composer composerProto = ComposerMapper.toProtobuf(composerOpt.get()); + + ComposerMessages.GetComposerByIdResponse getByIdResponse = ComposerMessages.GetComposerByIdResponse + .newBuilder().setComposer(composerProto).build(); + + return TransportProtocol.Response.newBuilder().setPayload(getByIdResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting composer by ID", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerHandler.java new file mode 100644 index 0000000..0d41487 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/composer/GetComposerHandler.java @@ -0,0 +1,50 @@ +package com.mediamanager.service.delegate.handler.composer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.ComposerMapper; +import com.mediamanager.model.Composer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ComposerMessages; +import com.mediamanager.protocol.messages.GenreMessages; +import com.mediamanager.service.composer.ComposerService; +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( "composer.getAll") +public class GetComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(GetComposerHandler.class); + private final ComposerService composerService; + + public GetComposerHandler(ComposerService composerService){ + this.composerService = composerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + try{ + List composers = composerService.getAllComposers(); + + ComposerMessages.GetComposersResponse.Builder responseBuilder = ComposerMessages.GetComposersResponse.newBuilder(); + for(Composer composer : composers){ + ComposerMessages.Composer composerProto = ComposerMapper.toProtobuf(composer); + responseBuilder.addComposers(composerProto); + } + ComposerMessages.GetComposersResponse getGenresResponse = responseBuilder.build(); + return TransportProtocol.Response.newBuilder().setPayload(getGenresResponse.toByteString()); + } catch (Exception e) { + logger.error("Error getting composers", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + + } + +} diff --git a/src/main/java/com/mediamanager/service/delegate/handler/composer/UpdateComposerHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/composer/UpdateComposerHandler.java new file mode 100644 index 0000000..d81af26 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/composer/UpdateComposerHandler.java @@ -0,0 +1,56 @@ +package com.mediamanager.service.delegate.handler.composer; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.mapper.ComposerMapper; +import com.mediamanager.model.Composer; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.protocol.messages.ComposerMessages; +import com.mediamanager.service.composer.ComposerService; +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("composer.update") +public class UpdateComposerHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(UpdateComposerHandler.class); + private final ComposerService composerService; + public UpdateComposerHandler(ComposerService composerService){ + this.composerService = composerService; + } + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + try{ + ComposerMessages.UpdateComposerRequest updateRequest = ComposerMessages.UpdateComposerRequest + .parseFrom(requestPayload); + int id = updateRequest.getId(); + String newName = updateRequest.getName(); + Optional composerOpt = composerService.updateComposer(id, newName); + if (composerOpt.isEmpty()) { + logger.warn("Composer not found with ID: {}", id); + return TransportProtocol.Response.newBuilder() + .setStatusCode(404) + .setPayload(ByteString.copyFromUtf8("Composer not found")); + } + ComposerMessages.Composer composerProto = ComposerMapper.toProtobuf(composerOpt.get()); + ComposerMessages.UpdateComposerResponse updateResponse = ComposerMessages.UpdateComposerResponse + .newBuilder().setComposer(composerProto).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 composer", e); + return TransportProtocol.Response.newBuilder() + .setStatusCode(500) + .setPayload(ByteString.copyFromUtf8("Error: " + e.getMessage())); + } + } +} 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..569b119 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/CreateGenreHandler.java @@ -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())); + } + } +} \ 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..bffdb1e --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/DeleteGenreHandler.java @@ -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()); + } + } +} \ 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..4dc4c71 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreByIdHandler.java @@ -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 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..068b44c --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/GetGenreHandler.java @@ -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 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..d0b54d1 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/genre/UpdateGenreHandler.java @@ -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 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/java/com/mediamanager/service/ipc/IPCManager.java b/src/main/java/com/mediamanager/service/ipc/IPCManager.java new file mode 100644 index 0000000..7fe7541 --- /dev/null +++ b/src/main/java/com/mediamanager/service/ipc/IPCManager.java @@ -0,0 +1,337 @@ +package com.mediamanager.service.ipc; + +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.service.delegate.DelegateActionManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class IPCManager { + private final Properties configuration; + private static final Logger logger = LogManager.getLogger(IPCManager.class); + private final DelegateActionManager actionManager; + + private Path socketPath; + private UnixDomainSocketAddress socketAddress; + private ServerSocketChannel serverChannel; + private ExecutorService clientThreadPool; + private final AtomicBoolean running = new AtomicBoolean(false); + private final ConcurrentHashMap activeClients = new ConcurrentHashMap<>(); + private final AtomicInteger clientIdCounter = new AtomicInteger(0); + + + public IPCManager(Properties config, DelegateActionManager actionManager){ + configuration = config; + this.actionManager = actionManager; + logger.debug("IPCManager created with configuration:"); + + + } + public void init() throws Exception { + logger.info("Initializing IPC connection..."); + validateConfiguration(); + socketPath = Path.of(configuration.getProperty("ipc.socket.path")).resolve("mediamanager.sock"); + + if (Files.exists(socketPath)) { + logger.warn("Socket file already exists at: {}", socketPath); + logger.info("Deleting existing socket..."); + Files.deleteIfExists(socketPath); + } + + Path parentDir = socketPath.getParent(); + if (parentDir != null && !Files.exists(parentDir)) { + logger.info("Creating parent directory for socket: {}", parentDir); + Files.createDirectories(parentDir); + } + + try { + socketAddress = UnixDomainSocketAddress.of(socketPath); + logger.debug("Socket address created"); + + serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + serverChannel.bind(socketAddress); + logger.info("Server bound to socket - file created at: {}", socketPath); + + // ESTA É A MUDANÇA CRÍTICA + // Configura o canal para modo não-bloqueante + // Isso faz accept() retornar null imediatamente se não houver cliente + // ao invés de bloquear esperando indefinidamente + serverChannel.configureBlocking(false); + logger.debug("Server channel configured for non-blocking mode"); + + Set perms = PosixFilePermissions.fromString("rw-------"); + Files.setPosixFilePermissions(socketPath, perms); + logger.debug("Socket permissions set to: rw-------"); + + clientThreadPool = Executors.newCachedThreadPool(runnable -> { + Thread thread = new Thread(runnable); + thread.setName("IPC-Client-Handler-" + thread.getId()); + thread.setDaemon(true); + return thread; + }); + logger.debug("Client thread pool created"); + + running.set(true); + + Thread serverThread = new Thread(this::acceptConnectionsLoop, "IPC-Server-Accept-Thread"); + serverThread.setDaemon(true); + serverThread.start(); + logger.info("Server thread started - accepting connections"); + + logger.info("IPC server initialized successfully on {}", socketPath.toAbsolutePath()); + + } catch (IOException e) { + logger.error("Failed to initialize IPC server: {}", e.getMessage()); + throw new Exception("Failed to initialize IPC server: " + e.getMessage(), e); + } + } + private void validateConfiguration() throws Exception { + String[] requiredProperties = { + "ipc.socket.path" + }; + for (String property : requiredProperties) { + if (configuration.getProperty(property) == null) { + throw new Exception("Missing required configuration property: " + property); + } + } + logger.debug("IPC configuration validated successfully"); + } + + public Path getSocketPath(){ + return socketPath; + } + + public void close() throws Exception { + if(!running.get()){ + logger.warn("IPC connection is already closed"); + } + + + logger.info("Closing IPC connection..."); + + running.set(false); + if (serverChannel != null && serverChannel.isOpen()) { + serverChannel.close(); + logger.debug("Server channel closed"); + } + + if (clientThreadPool != null) { + clientThreadPool.shutdown(); + try { + if (!clientThreadPool.awaitTermination(30, TimeUnit.SECONDS)) { + logger.warn("Some client handlers did not finish in time, forcing shutdown"); + clientThreadPool.shutdownNow(); + } + } catch (InterruptedException e) { + logger.error("Interrupted while waiting for client handlers", e); + clientThreadPool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + if (socketPath != null && Files.exists(socketPath)) { + Files.deleteIfExists(socketPath); + logger.info("Socket file deleted successfully"); + } + + logger.info("IPC server closed successfully"); + } + + private void acceptConnectionsLoop() { + logger.info("Preparing to accept connections..."); + + while (running.get()) { + try { + // Em modo não-bloqueante, accept() retorna imediatamente + // Se há um cliente esperando, retorna o SocketChannel + // Se não há cliente, retorna null + SocketChannel clientChannel = serverChannel.accept(); + + if (clientChannel != null) { + // Um cliente realmente se conectou! + int clientId = clientIdCounter.incrementAndGet(); + logger.info("Client {} connected", clientId); + + ClientHandler handler = new ClientHandler(clientId, clientChannel); + activeClients.put(clientId, handler); + + clientThreadPool.submit(() -> { + try { + handler.handle(); + } finally { + activeClients.remove(clientId); + logger.info("Client {} disconnected", clientId); + } + }); + } else { + // Nenhum cliente conectado no momento + // Dorme por um curto período antes de verificar novamente + // Isso evita consumir CPU desnecessariamente em um loop vazio + Thread.sleep(1); // 1 milissegundos + } + + } catch (InterruptedException e) { + // Thread foi interrompida, provavelmente durante shutdown + logger.debug("Accept loop interrupted"); + break; + + } catch (IOException e) { + // Erros de I/O reais devem ser logados + if (running.get()) { + logger.error("Error accepting client connection", e); + } + break; + } + } + + logger.info("Connection loop stopped gracefully"); + } + + + private class ClientHandler { + private final int clientId; + private final SocketChannel channel; + + public ClientHandler(int clientId, SocketChannel channel) { + this.clientId = clientId; + this.channel = channel; + } + + /** + * Método principal que processa a comunicação com o cliente. + * Aqui é onde vamos ler mensagens JSON, processá-las, e enviar respostas. + */ + public void handle() { + logger.debug("Client {} handler thread started", clientId); + + try { + // LOOP: processa múltiplas requests na mesma conexão + while (channel.isOpen()) { + TransportProtocol.Request request = readRequest(channel); + + if (request == null) { + // Cliente desconectou gracefully + logger.info("Client {} disconnected (end of stream)", clientId); + break; + } + + logger.info("Client {} sent request {}", clientId, request.getRequestId()); + + // Processa usando o DelegateActionManager + TransportProtocol.Response response = actionManager.ProcessedRequest(request); + + // Envia resposta de volta + writeResponse(channel, response); + logger.info("Client {} response sent", clientId); + + // Verifica se é comando CLOSE + String connectionHeader = response.getHeadersOrDefault("Connection", ""); + if ("close".equals(connectionHeader)) { + logger.info("Client {} requested connection close", clientId); + break; // Sai do loop e fecha + } + } + + } catch (IOException e) { + if (channel.isOpen()) { + logger.error("IO error handling client {}", clientId, e); + } else { + logger.debug("Client {} connection closed by peer", clientId); + } + } catch (Exception e) { + logger.error("Unexpected error handling client {}", clientId, e); + } finally { + try { + if (channel.isOpen()) { + channel.close(); + } + logger.debug("Client {} channel closed", clientId); + } catch (IOException e) { + logger.error("Error closing client {} channel", clientId, e); + } + } + } + private TransportProtocol.Request readRequest(SocketChannel channel) throws IOException { + // Primeiro, lê o tamanho da mensagem (4 bytes = int32) + java.nio.ByteBuffer sizeBuffer = java.nio.ByteBuffer.allocate(4); + int bytesRead = 0; + + while (bytesRead < 4) { + int read = channel.read(sizeBuffer); + if (read == -1) { + logger.debug("Client disconnected before sending size"); + return null; + } + bytesRead += read; + } + + sizeBuffer.flip(); + int messageSize = sizeBuffer.getInt(); + logger.debug("Expecting message of {} bytes", messageSize); + + // Validação básica de segurança + if (messageSize <= 0 || messageSize > 1024 * 1024) { // Max 1MB + throw new IOException("Invalid message size: " + messageSize); + } + + // Agora lê a mensagem completa + java.nio.ByteBuffer messageBuffer = java.nio.ByteBuffer.allocate(messageSize); + bytesRead = 0; + + while (bytesRead < messageSize) { + int read = channel.read(messageBuffer); + if (read == -1) { + throw new IOException("Client disconnected while reading message"); + } + bytesRead += read; + } + + messageBuffer.flip(); + + // Deserializa o Protocol Buffers + byte[] messageBytes = new byte[messageSize]; + messageBuffer.get(messageBytes); + + return TransportProtocol.Request.parseFrom(messageBytes); + } + + private void writeResponse(SocketChannel channel, TransportProtocol.Response response) throws IOException { + byte[] messageBytes = response.toByteArray(); + int messageSize = messageBytes.length; + + logger.debug("Writing response of {} bytes", messageSize); + + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(4 + messageSize); + buffer.putInt(messageSize); + buffer.put(messageBytes); + buffer.flip(); + + while (buffer.hasRemaining()) { + channel.write(buffer); + } + + logger.debug("Response written successfully"); + } + } +} + + + diff --git a/src/main/proto/artist.proto b/src/main/proto/artist.proto new file mode 100644 index 0000000..254e7f7 --- /dev/null +++ b/src/main/proto/artist.proto @@ -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; +} diff --git a/src/main/proto/bitdepth.proto b/src/main/proto/bitdepth.proto new file mode 100644 index 0000000..571dc9a --- /dev/null +++ b/src/main/proto/bitdepth.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "BitDepthMessages"; + +package mediamanager.messages; + +message BitDepth { + int32 id = 1; + string value = 2; +} + +message CreateBitDepthRequest { + string value = 1; +} + +message CreateBitDepthResponse { + BitDepth bitdepth = 1; +} + +message GetBitDepthsRequest { + +} + +message GetBitDepthsResponse { + repeated BitDepth bitdepths = 1; +} + +message GetBitDepthByIdRequest { + int32 id = 1; +} + +message GetBitDepthByIdResponse { + BitDepth bitdepth = 1; +} + +message UpdateBitDepthRequest { + int32 id = 1; + string value = 2; // Novo nome +} + +message UpdateBitDepthResponse { + BitDepth bitdepth = 1; +} + +message DeleteBitDepthRequest { + int32 id = 1; +} + +message DeleteBitDepthResponse { + bool success = 1; + string message = 2; +} diff --git a/src/main/proto/composer.proto b/src/main/proto/composer.proto new file mode 100644 index 0000000..ce82fba --- /dev/null +++ b/src/main/proto/composer.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol.messages"; +option java_outer_classname = "ComposerMessages"; + +package mediamanager.messages; + +message Composer { + int32 id =1; + string name =2; +} + +message CreateComposerRequest{ + string name =1; +} + +message CreateComposerResponse{ + Composer composer = 1; +} + +message GetComposersRequest{} + +message GetComposersResponse{ + repeated Composer composers = 1; +} + +message GetComposerByIdRequest{ + int32 id =1; +} + +message GetComposerByIdResponse{ + Composer composer =1; +} + +message UpdateComposerRequest{ + int32 id = 1; + string name=2; +} + +message UpdateComposerResponse{ + Composer composer = 1; +} + +message DeleteComposerRequest{ + int32 id = 1; +} + +message DeleteComposerResponse{ + bool success = 1; + string message = 2; +} 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; +} diff --git a/src/main/proto/messages.proto b/src/main/proto/messages.proto new file mode 100644 index 0000000..106e59e --- /dev/null +++ b/src/main/proto/messages.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol"; +option java_outer_classname = "TransportProtocol"; + +package mediamanager; + +message Request { + string request_id = 1; + bytes payload = 2; + map headers = 3; +} + +message Response { + string request_id = 1; + int32 status_code = 2; + bytes payload = 3; + map headers = 4; +} \ No newline at end of file diff --git a/src/main/proto/test.proto b/src/main/proto/test.proto new file mode 100644 index 0000000..00330cd --- /dev/null +++ b/src/main/proto/test.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +option java_package = "com.mediamanager.protocol"; +option java_outer_classname = "TestProtocol"; + +package mediamanager.test; + +message EchoCommand { + string message = 1; +} + +message EchoResponse { + string message = 1; + int64 server_timestamp = 2; +} + +message HeartbeatCommand { + int64 client_timestamp = 1; +} + +message HeartbeatResponse { + int64 client_timestamp = 1; + int64 server_timestamp = 2; +} +message CloseCommand { + // Vazio - apenas sinaliza fechamento +} +message CloseResponse { + string message = 1; +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/config.properties.example similarity index 92% rename from src/main/resources/application.properties rename to src/main/resources/config.properties.example index fbef11a..0991905 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/config.properties.example @@ -21,3 +21,8 @@ hibernate.format_sql=true ipc.pipe.name=mediamanager-pipe ipc.pipe.path=/tmp/mediamanager ipc.buffer.size=8192 +runtype=local +ipc.socket.path=/tmp/mediamanager + + + diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 29d32dc..a9f3f84 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -1,12 +1,13 @@ - + - + + filePattern="logs/mediamanager-%d{yyyy-MM-dd}-%i.log" + immediateFlush="true"> @@ -17,17 +18,17 @@ - + - + - +