From 98483338b41dc8e621a2fd5aa16c0c7f174d1ddb Mon Sep 17 00:00:00 2001 From: John Ahlroos Date: Thu, 13 Nov 2025 13:05:46 +0100 Subject: [PATCH] Modernize Feedback service --- FeedbackService/build.gradle | 7 ++ .../feedback/PausableThreadPoolExecutor.java | 51 ++++++++++++++ .../feedback/SentimentAnalyzer.java | 63 ++++------------- .../controller/FeedbackController.java | 7 +- .../feedback/model/EnhancedFeedback.java | 25 ++----- .../feedback/model/FeedbackEntry.java | 58 ++++++--------- .../feedback/model/FeedbackSummary.java | 45 +++--------- .../feedback/service/FeedbackService.java | 70 +++++++++++-------- .../feedback/service/GeminiService.java | 30 ++++---- 9 files changed, 172 insertions(+), 184 deletions(-) create mode 100644 FeedbackService/src/main/java/com/retailstore/feedback/PausableThreadPoolExecutor.java diff --git a/FeedbackService/build.gradle b/FeedbackService/build.gradle index ecbd0c5..b340bab 100644 --- a/FeedbackService/build.gradle +++ b/FeedbackService/build.gradle @@ -37,4 +37,11 @@ dependencies { runtimeOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + compileOnly("org.projectlombok:lombok:1.18.42") + annotationProcessor("org.projectlombok:lombok:1.18.42") + + + testCompileOnly("org.projectlombok:lombok:1.18.42") + testAnnotationProcessor("org.projectlombok:lombok:1.18.42") } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/PausableThreadPoolExecutor.java b/FeedbackService/src/main/java/com/retailstore/feedback/PausableThreadPoolExecutor.java new file mode 100644 index 0000000..9d5f052 --- /dev/null +++ b/FeedbackService/src/main/java/com/retailstore/feedback/PausableThreadPoolExecutor.java @@ -0,0 +1,51 @@ +package com.retailstore.feedback; + +import java.util.concurrent.*; +import java.util.concurrent.locks.*; + +/** + * Thread pool executor that allows for pausing and resuming tasks + */ +public class PausableThreadPoolExecutor extends ThreadPoolExecutor { + private boolean isPaused; + private final ReentrantLock pauseLock = new ReentrantLock(); + private final Condition unpaused = pauseLock.newCondition(); + + public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingDeque<>()); + } + + @Override + protected void beforeExecute(Thread t, Runnable r) { + super.beforeExecute(t, r); + pauseLock.lock(); + try { + while (isPaused) { + unpaused.await(); + } + } catch (InterruptedException ie) { + t.interrupt(); + } finally { + pauseLock.unlock(); + } + } + + public void pause() { + pauseLock.lock(); + try { + isPaused = true; + } finally { + pauseLock.unlock(); + } + } + + public void resume() { + pauseLock.lock(); + try { + isPaused = false; + unpaused.signalAll(); + } finally { + pauseLock.unlock(); + } + } +} \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/SentimentAnalyzer.java b/FeedbackService/src/main/java/com/retailstore/feedback/SentimentAnalyzer.java index 7d57ecb..84f22d8 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/SentimentAnalyzer.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/SentimentAnalyzer.java @@ -1,5 +1,7 @@ package com.retailstore.feedback; +import com.retailstore.feedback.model.FeedbackEntry; +import com.retailstore.feedback.model.FeedbackEntry.FeedbackEntryBuilder; import edu.stanford.nlp.pipeline.CoreDocument; import edu.stanford.nlp.pipeline.CoreSentence; import edu.stanford.nlp.pipeline.StanfordCoreNLP; @@ -117,9 +119,8 @@ public class SentimentAnalyzer { if (feedbackEntry != null) { // Analyze sentiment log.info("Analyzing sentiment for entry #{}", feedbackEntry.getId()); - String sentiment = analyzeSentiment(feedbackEntry.comment); - feedbackEntry.setSentiment(sentiment); - + String sentiment = analyzeSentiment(feedbackEntry.getComment()); + feedbackEntry = feedbackEntry.withSentiment(sentiment); feedbackEntries.add(feedbackEntry); } else { log.error("Failed to parse entry at position {}", i); @@ -136,7 +137,7 @@ public class SentimentAnalyzer { * @return FeedbackEntry object */ private FeedbackEntry parseEntry(String entryText) { - FeedbackEntry entry = new FeedbackEntry(); + FeedbackEntryBuilder entryBuilder = FeedbackEntry.builder(); try { // Split into lines for easier parsing @@ -150,7 +151,7 @@ public class SentimentAnalyzer { if (line.startsWith("Feedback #")) { Matcher numberMatcher = FEEDBACK_NUMBER_PATTERN.matcher(line); if (numberMatcher.find()) { - entry.setId(Integer.parseInt(numberMatcher.group(1))); + entryBuilder.id(Integer.parseInt(numberMatcher.group(1))); } } @@ -158,7 +159,7 @@ public class SentimentAnalyzer { else if (line.startsWith("Customer:")) { Matcher customerMatcher = CUSTOMER_PATTERN.matcher(line); if (customerMatcher.find()) { - entry.setCustomer(customerMatcher.group(1).trim()); + entryBuilder.customer(customerMatcher.group(1).trim()); } } @@ -166,7 +167,7 @@ public class SentimentAnalyzer { else if (line.startsWith("Department:")) { Matcher departmentMatcher = DEPARTMENT_PATTERN.matcher(line); if (departmentMatcher.find()) { - entry.setDepartment(departmentMatcher.group(1).trim()); + entryBuilder.department(departmentMatcher.group(1).trim()); } } @@ -174,7 +175,7 @@ public class SentimentAnalyzer { else if (line.startsWith("Date:")) { Matcher dateMatcher = DATE_PATTERN.matcher(line); if (dateMatcher.find()) { - entry.setDate(dateMatcher.group(1).trim()); + entryBuilder.date(dateMatcher.group(1).trim()); } } @@ -182,7 +183,7 @@ public class SentimentAnalyzer { else if (line.startsWith("Comment:")) { Matcher commentMatcher = COMMENT_PATTERN.matcher(line); if (commentMatcher.find()) { - entry.setComment(commentMatcher.group(1).trim()); + entryBuilder.comment(commentMatcher.group(1).trim()); } } @@ -190,7 +191,8 @@ public class SentimentAnalyzer { } // Validate that we have all required fields - if (entry.getId() > 0 && entry.getComment() != null) { + FeedbackEntry entry = entryBuilder.build(); + if (entry.validate()) { return entry; } else { log.error("Invalid entry - missing ID or comment"); @@ -212,6 +214,7 @@ public class SentimentAnalyzer { * @return Sentiment (VERY_POSITIVE, POSITIVE, NEUTRAL, NEGATIVE, VERY_NEGATIVE) */ public String analyzeSentiment(String comment) { + // Create a document from the comment CoreDocument doc = new CoreDocument(comment); @@ -300,44 +303,4 @@ public class SentimentAnalyzer { } } } - - /** - * Inner class representing a feedback entry with sentiment analysis - */ - public static class FeedbackEntry { - private int id; - private String customer; - private String department; - private String date; - private String comment; - private String sentiment; - - // Default values to avoid null - public FeedbackEntry() { - this.customer = ""; - this.department = ""; - this.date = ""; - this.comment = ""; - this.sentiment = ""; - } - - // Getters and setters - public int getId() { return id; } - public void setId(int id) { this.id = id; } - - public String getCustomer() { return customer; } - public void setCustomer(String customer) { this.customer = customer; } - - public String getDepartment() { return department; } - public void setDepartment(String department) { this.department = department; } - - public String getDate() { return date; } - public void setDate(String date) { this.date = date; } - - public String getComment() { return comment; } - public void setComment(String comment) { this.comment = comment; } - - public String getSentiment() { return sentiment; } - public void setSentiment(String sentiment) { this.sentiment = sentiment; } - } } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/controller/FeedbackController.java b/FeedbackService/src/main/java/com/retailstore/feedback/controller/FeedbackController.java index 1ca643d..f68c3f8 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/controller/FeedbackController.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/controller/FeedbackController.java @@ -20,8 +20,11 @@ import java.util.List; @Controller public class FeedbackController { - @Autowired - private FeedbackService feedbackService; + private final FeedbackService feedbackService; + + public FeedbackController(FeedbackService feedbackService) { + this.feedbackService = feedbackService; + } /** * Displays the dashboard page. diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/model/EnhancedFeedback.java b/FeedbackService/src/main/java/com/retailstore/feedback/model/EnhancedFeedback.java index b94e671..6c11882 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/model/EnhancedFeedback.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/model/EnhancedFeedback.java @@ -1,29 +1,14 @@ package com.retailstore.feedback.model; +import lombok.Getter; +import lombok.experimental.SuperBuilder; + /** * Represents feedback enhanced with AI-generated category and actionable insights. */ +@Getter +@SuperBuilder public class EnhancedFeedback extends FeedbackEntry { private String category; private String actionableInsight; - - // Default constructor - public EnhancedFeedback() { - super(); - } - - // Constructor based on FeedbackEntry - public EnhancedFeedback(FeedbackEntry entry) { - super(entry.getId(), entry.getCustomer(), entry.getDepartment(), - entry.getDate(), entry.getComment(), entry.getSentiment()); - } - - // Getters and setters for additional fields - public String getCategory() { return category; } - public void setCategory(String category) { this.category = category; } - - public String getActionableInsight() { return actionableInsight; } - public void setActionableInsight(String actionableInsight) { - this.actionableInsight = actionableInsight; - } } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackEntry.java b/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackEntry.java index 1a9d5a4..6d76cba 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackEntry.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackEntry.java @@ -1,46 +1,34 @@ package com.retailstore.feedback.model; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.With; +import lombok.experimental.SuperBuilder; + /** * Represents a feedback entry with sentiment analysis. */ +@Getter +@SuperBuilder +@AllArgsConstructor public class FeedbackEntry { + private int id; - private String customer; - private String department; - private String date; - private String comment; - private String sentiment; - // Default constructor - public FeedbackEntry() {} + @Builder.Default + private String customer = ""; + @Builder.Default + private String department = ""; + @Builder.Default + private String date = ""; + @Builder.Default + private String comment = ""; + @With + @Builder.Default + private String sentiment = ""; - // Constructor with parameters - public FeedbackEntry(int id, String customer, String department, - String date, String comment, String sentiment) { - this.id = id; - this.customer = customer; - this.department = department; - this.date = date; - this.comment = comment; - this.sentiment = sentiment; + public boolean validate() { + return id > 0 && comment != null; } - - // Getters and setters - public int getId() { return id; } - public void setId(int id) { this.id = id; } - - public String getCustomer() { return customer; } - public void setCustomer(String customer) { this.customer = customer; } - - public String getDepartment() { return department; } - public void setDepartment(String department) { this.department = department; } - - public String getDate() { return date; } - public void setDate(String date) { this.date = date; } - - public String getComment() { return comment; } - public void setComment(String comment) { this.comment = comment; } - - public String getSentiment() { return sentiment; } - public void setSentiment(String sentiment) { this.sentiment = sentiment; } } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackSummary.java b/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackSummary.java index 8a445bd..e8eb4b9 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackSummary.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/model/FeedbackSummary.java @@ -1,44 +1,21 @@ package com.retailstore.feedback.model; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + import java.util.List; import java.util.Map; /** * Represents a summary of feedback data for the dashboard. */ +@Getter +@Builder public class FeedbackSummary { - private int totalFeedback; - private Map sentimentCounts; - private Map categoryCounts; - private Map departmentCounts; - private List recentFeedback; - - // Default constructor - public FeedbackSummary() {} - - // Getters and setters - public int getTotalFeedback() { return totalFeedback; } - public void setTotalFeedback(int totalFeedback) { - this.totalFeedback = totalFeedback; - } - - public Map getSentimentCounts() { return sentimentCounts; } - public void setSentimentCounts(Map sentimentCounts) { - this.sentimentCounts = sentimentCounts; - } - - public Map getCategoryCounts() { return categoryCounts; } - public void setCategoryCounts(Map categoryCounts) { - this.categoryCounts = categoryCounts; - } - - public Map getDepartmentCounts() { return departmentCounts; } - public void setDepartmentCounts(Map departmentCounts) { - this.departmentCounts = departmentCounts; - } - - public List getRecentFeedback() { return recentFeedback; } - public void setRecentFeedback(List recentFeedback) { - this.recentFeedback = recentFeedback; - } + private final int totalFeedback; + private final Map sentimentCounts; + private final Map categoryCounts; + private final Map departmentCounts; + private final List recentFeedback; } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/service/FeedbackService.java b/FeedbackService/src/main/java/com/retailstore/feedback/service/FeedbackService.java index 480f0c6..3a49108 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/service/FeedbackService.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/service/FeedbackService.java @@ -5,6 +5,9 @@ import com.retailstore.feedback.model.FeedbackEntry; import com.retailstore.feedback.model.EnhancedFeedback; import com.retailstore.feedback.model.FeedbackSummary; +import com.retailstore.feedback.model.FeedbackSummary.FeedbackSummaryBuilder; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +15,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -82,7 +86,7 @@ public class FeedbackService { } // Process each line - if (line.trim().isEmpty() && entryText.length() > 0) { + if (line.trim().isEmpty() && !entryText.isEmpty()) { // We've reached the end of an entry FeedbackEntry entry = parseFeedbackEntry(entryText.toString()); if (entry != null) { @@ -95,7 +99,7 @@ public class FeedbackService { } // Process the last entry if there is one - if (entryText.length() > 0) { + if (!entryText.isEmpty()) { FeedbackEntry entry = parseFeedbackEntry(entryText.toString()); if (entry != null) { entries.add(entry); @@ -113,12 +117,12 @@ public class FeedbackService { * @return FeedbackEntry object or null if parsing fails */ private FeedbackEntry parseFeedbackEntry(String text) { - FeedbackEntry entry = new FeedbackEntry(); + FeedbackEntry.FeedbackEntryBuilder entryBuilder = FeedbackEntry.builder(); // Extract feedback ID Matcher idMatcher = FEEDBACK_PATTERN.matcher(text); if (idMatcher.find()) { - entry.setId(Integer.parseInt(idMatcher.group(1))); + entryBuilder.id(Integer.parseInt(idMatcher.group(1))); } else { return null; } @@ -126,34 +130,34 @@ public class FeedbackService { // Extract customer Matcher customerMatcher = CUSTOMER_PATTERN.matcher(text); if (customerMatcher.find()) { - entry.setCustomer(customerMatcher.group(1)); + entryBuilder.customer(customerMatcher.group(1)); } // Extract department Matcher departmentMatcher = DEPARTMENT_PATTERN.matcher(text); if (departmentMatcher.find()) { - entry.setDepartment(departmentMatcher.group(1)); + entryBuilder.department(departmentMatcher.group(1)); } // Extract date Matcher dateMatcher = DATE_PATTERN.matcher(text); if (dateMatcher.find()) { - entry.setDate(dateMatcher.group(1)); + entryBuilder.date(dateMatcher.group(1)); } // Extract comment Matcher commentMatcher = COMMENT_PATTERN.matcher(text); if (commentMatcher.find()) { - entry.setComment(commentMatcher.group(1)); + entryBuilder.comment(commentMatcher.group(1)); } // Extract sentiment Matcher sentimentMatcher = SENTIMENT_PATTERN.matcher(text); if (sentimentMatcher.find()) { - entry.setSentiment(sentimentMatcher.group(1)); + entryBuilder.sentiment(sentimentMatcher.group(1)); } - return entry; + return entryBuilder.build(); } /** @@ -162,14 +166,16 @@ public class FeedbackService { * @return List of EnhancedFeedback objects * @throws IOException If an I/O error occurs */ - public synchronized List getEnhancedFeedback() throws IOException { + @EventListener(ApplicationStartedEvent.class) + public List getEnhancedFeedback() throws IOException { // Return cached data if available if (enhancedFeedbackCache != null) { return enhancedFeedbackCache; } + enhancedFeedbackCache = Collections.emptyList(); - List entries = readFeedbackData(); List enhancedEntries = new ArrayList<>(); + List entries = readFeedbackData(); for (FeedbackEntry entry : entries) { EnhancedFeedback enhancedEntry = enhanceFeedback(entry); @@ -189,14 +195,20 @@ public class FeedbackService { * @return EnhancedFeedback with AI-generated category and actionable insight */ private EnhancedFeedback enhanceFeedback(FeedbackEntry entry) { - EnhancedFeedback enhancedEntry = new EnhancedFeedback(entry); + EnhancedFeedback.EnhancedFeedbackBuilder enhancedEntryBuilder = EnhancedFeedback.builder() + .id(entry.getId()) + .date(entry.getDate()) + .comment(entry.getComment()) + .customer(entry.getCustomer()) + .department(entry.getDepartment()) + .sentiment(entry.getSentiment()); // Create a prompt for Gemini String prompt = PROMPT.formatted(entry.getComment(), entry.getDepartment(), entry.getSentiment()); try { // Call Gemini API and parse the response - String response = geminiService.generateContent(prompt); + String response = geminiService.generateContent(prompt).get(3, TimeUnit.SECONDS); // Parse JSON response // This is a simple parsing approach - for production, use a proper JSON parser @@ -206,27 +218,27 @@ public class FeedbackService { Pattern categoryPattern = Pattern.compile("\"category\"\\s*:\\s*\"([^\"]+)\""); Matcher categoryMatcher = categoryPattern.matcher(jsonResponse); if (categoryMatcher.find()) { - enhancedEntry.setCategory(categoryMatcher.group(1)); + enhancedEntryBuilder.category(categoryMatcher.group(1)); } else { - enhancedEntry.setCategory("Uncategorized"); + enhancedEntryBuilder.category("Uncategorized"); } // Extract actionable insight Pattern insightPattern = Pattern.compile("\"actionableInsight\"\\s*:\\s*\"([^\"]+)\""); Matcher insightMatcher = insightPattern.matcher(jsonResponse); if (insightMatcher.find()) { - enhancedEntry.setActionableInsight(insightMatcher.group(1)); + enhancedEntryBuilder.actionableInsight(insightMatcher.group(1)); } else { - enhancedEntry.setActionableInsight("No specific action recommended."); + enhancedEntryBuilder.actionableInsight("No specific action recommended."); } } catch (Exception e) { // Handle API errors gracefully - enhancedEntry.setCategory("Error in processing"); - enhancedEntry.setActionableInsight("Could not generate insight due to API error: " + e.getMessage()); + enhancedEntryBuilder.category("Error in processing"); + enhancedEntryBuilder.actionableInsight("Could not generate insight due to API error: " + e.getMessage()); } - return enhancedEntry; + return enhancedEntryBuilder.build(); } /** @@ -237,10 +249,8 @@ public class FeedbackService { */ public FeedbackSummary generateFeedbackSummary() throws IOException { List allFeedback = getEnhancedFeedback(); - FeedbackSummary summary = new FeedbackSummary(); - - // Set total feedback count - summary.setTotalFeedback(allFeedback.size()); + FeedbackSummaryBuilder summaryBuilder = FeedbackSummary.builder() + .totalFeedback(allFeedback.size()); // Count sentiments Map sentimentCounts = new HashMap<>(); @@ -248,7 +258,7 @@ public class FeedbackService { String sentiment = feedback.getSentiment(); sentimentCounts.put(sentiment, sentimentCounts.getOrDefault(sentiment, 0) + 1); } - summary.setSentimentCounts(sentimentCounts); + summaryBuilder.sentimentCounts(sentimentCounts); // Count categories Map categoryCounts = new HashMap<>(); @@ -256,7 +266,7 @@ public class FeedbackService { String category = feedback.getCategory(); categoryCounts.put(category, categoryCounts.getOrDefault(category, 0) + 1); } - summary.setCategoryCounts(categoryCounts); + summaryBuilder.categoryCounts(categoryCounts); // Count departments Map departmentCounts = new HashMap<>(); @@ -264,15 +274,15 @@ public class FeedbackService { String department = feedback.getDepartment(); departmentCounts.put(department, departmentCounts.getOrDefault(department, 0) + 1); } - summary.setDepartmentCounts(departmentCounts); + summaryBuilder.departmentCounts(departmentCounts); // Get recent feedback (last 5 entries) List recentFeedback = allFeedback.stream() .sorted(Comparator.comparing(EnhancedFeedback::getId).reversed()) .limit(5) .collect(Collectors.toList()); - summary.setRecentFeedback(recentFeedback); + summaryBuilder.recentFeedback(recentFeedback); - return summary; + return summaryBuilder.build(); } } \ No newline at end of file diff --git a/FeedbackService/src/main/java/com/retailstore/feedback/service/GeminiService.java b/FeedbackService/src/main/java/com/retailstore/feedback/service/GeminiService.java index f550ebe..f4a03f9 100644 --- a/FeedbackService/src/main/java/com/retailstore/feedback/service/GeminiService.java +++ b/FeedbackService/src/main/java/com/retailstore/feedback/service/GeminiService.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.retailstore.feedback.PausableThreadPoolExecutor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.client.ClientHttpRequestExecution; @@ -16,18 +18,23 @@ import org.springframework.http.*; import org.springframework.web.util.UriComponentsBuilder; import java.io.IOException; +import java.util.Queue; +import java.util.concurrent.*; /** * Service for interacting with Google's Gemini AI using REST API */ +@Slf4j @Service public class GeminiService { private final RestTemplate restTemplate; private final ObjectMapper objectMapper; - private final String serviceUri; + private static final PausableThreadPoolExecutor promptExecution = new PausableThreadPoolExecutor(1,1,30, TimeUnit.SECONDS); + private static final ScheduledExecutorService scheduleExecution = Executors.newSingleThreadScheduledExecutor(); + public GeminiService(@Value("${gemini.api.url}") String url, @Value("${gemini.api.key}") String apiKey) { this.objectMapper = new JsonMapper(); serviceUri = UriComponentsBuilder @@ -45,7 +52,11 @@ public class GeminiService { * @param prompt The prompt to send to Gemini * @return The response from Gemini */ - public String generateContent(String prompt) { + public Future generateContent(String prompt) { + return promptExecution.submit(() -> executePrompt(prompt)); + } + + private String executePrompt(String prompt) { try { // Create the request body using Jackson ObjectNode requestBody = objectMapper.createObjectNode(); @@ -91,18 +102,11 @@ public class GeminiService { return "No response from Gemini"; } catch (Exception e) { - System.err.println("Error calling Gemini API: " + e.getMessage()); - e.printStackTrace(); + log.error("Error calling Gemini API: {}", e.getMessage(), e); + log.info("Pausing Gemini API calls for 10 seconds..."); + promptExecution.pause(); + scheduleExecution.schedule(promptExecution::resume, 10, TimeUnit.SECONDS); return "Error: " + e.getMessage(); } } - - /** - * Test method to verify API connection - */ - public void testConnection() { - String testPrompt = "Say 'Hello, World!' if you can hear me."; - String response = generateContent(testPrompt); - System.out.println("Gemini API Test Response: " + response); - } } \ No newline at end of file