diff --git a/Everest.iml b/Everest.iml index ce957ca..d2de8f2 100644 --- a/Everest.iml +++ b/Everest.iml @@ -30,5 +30,11 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e69a32b..f71e7e0 100644 --- a/pom.xml +++ b/pom.xml @@ -102,5 +102,11 @@ sqlite-jdbc 3.21.0.1 + + + com.google.guava + guava + 24.1-jre + \ No newline at end of file diff --git a/src/main/java/com/rohitawate/everest/util/Services.java b/src/main/java/com/rohitawate/everest/util/Services.java index 219d1f8..d165e1a 100644 --- a/src/main/java/com/rohitawate/everest/util/Services.java +++ b/src/main/java/com/rohitawate/everest/util/Services.java @@ -16,21 +16,26 @@ package com.rohitawate.everest.util; +import com.google.common.util.concurrent.MoreExecutors; import com.rohitawate.everest.controllers.HomeWindowController; import com.rohitawate.everest.util.history.HistoryManager; import com.rohitawate.everest.util.logging.Level; import com.rohitawate.everest.util.logging.LoggingService; +import java.util.concurrent.Executor; + public class Services { public static Thread startServicesThread; public static HistoryManager historyManager; public static LoggingService loggingService; public static HomeWindowController homeWindowController; + public static Executor singleExecutor; public static void start() { startServicesThread = new Thread(() -> { loggingService = new LoggingService(Level.INFO); historyManager = new HistoryManager(); + singleExecutor = MoreExecutors.directExecutor(); }); startServicesThread.start(); diff --git a/src/main/java/com/rohitawate/everest/util/history/HistoryManager.java b/src/main/java/com/rohitawate/everest/util/history/HistoryManager.java index ce08ee1..3512860 100644 --- a/src/main/java/com/rohitawate/everest/util/history/HistoryManager.java +++ b/src/main/java/com/rohitawate/everest/util/history/HistoryManager.java @@ -25,6 +25,7 @@ import com.rohitawate.everest.util.settings.Settings; import javax.ws.rs.core.MediaType; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.sql.*; @@ -39,6 +40,7 @@ public class HistoryManager { private Connection conn; private JsonNode queries; private PreparedStatement statement; + private HistorySaver historySaver = new HistorySaver(); public HistoryManager() { try { @@ -47,31 +49,8 @@ public class HistoryManager { configFolder.mkdirs(); conn = DriverManager.getConnection("jdbc:sqlite:Everest/config/history.sqlite"); + initDatabase(); - // Read all queries from Queries.json - InputStream queriesFile = getClass().getResourceAsStream("/sql/Queries.json"); - ObjectMapper mapper = new ObjectMapper(); - queries = mapper.readTree(queriesFile); - - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("createRequestsTable").toString())); - statement.execute(); - - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("createHeadersTable").toString())); - statement.execute(); - - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("createRequestContentMapTable").toString())); - statement.execute(); - - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("createBodiesTable").toString())); - statement.execute(); - - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("createTuplesTable").toString())); - statement.execute(); } catch (Exception E) { Services.loggingService.logSevere("Exception while initializing HistoryManager.", E, LocalDateTime.now()); } finally { @@ -79,6 +58,39 @@ public class HistoryManager { } } + /** + * Creates and initializes the database with necessary tables if not already done. + * + * @throws IOException + * @throws SQLException + */ + private void initDatabase() throws IOException, SQLException { + // Read all queries from Queries.json + InputStream queriesFile = getClass().getResourceAsStream("/sql/Queries.json"); + ObjectMapper mapper = new ObjectMapper(); + queries = mapper.readTree(queriesFile); + + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("createRequestsTable").toString())); + statement.execute(); + + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("createHeadersTable").toString())); + statement.execute(); + + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("createRequestContentMapTable").toString())); + statement.execute(); + + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("createBodiesTable").toString())); + statement.execute(); + + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("createTuplesTable").toString())); + statement.execute(); + } + // Method is made synchronized to allow only one database transaction at a time. /** @@ -90,118 +102,8 @@ public class HistoryManager { if (isDuplicate(state)) return; - new Thread(() -> { - try { - statement = - conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequest").toString())); - - statement.setString(1, state.getHttpMethod()); - statement.setString(2, String.valueOf(state.getTarget())); - statement.setString(3, LocalDate.now().toString()); - - statement.executeUpdate(); - - // Get latest RequestID to insert into Headers table - statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests"); - - ResultSet RS = statement.executeQuery(); - int requestID = -1; - if (RS.next()) - requestID = RS.getInt("MaxID"); - - if (state.getHeaders().size() > 0) { - // Saves request headers - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveHeader").toString())); - for (Entry entry : state.getHeaders().entrySet()) { - statement.setInt(1, requestID); - statement.setString(2, entry.getKey().toString()); - statement.setString(3, entry.getValue().toString()); - - statement.executeUpdate(); - } - } - - if (state.getParams().size() > 0) { - // Saves request parameters - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); - for (Entry entry : state.getParams().entrySet()) { - statement.setInt(1, requestID); - statement.setString(2, "Param"); - statement.setString(3, entry.getKey().toString()); - statement.setString(4, entry.getValue().toString()); - - statement.executeUpdate(); - } - } - - if (!(state.getHttpMethod().equals("GET") || state.getHttpMethod().equals("DELETE"))) { - // Maps the request to its ContentType for faster recovery - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequestContentPair").toString())); - statement.setInt(1, requestID); - statement.setString(2, state.getContentType()); - - statement.executeUpdate(); - - // Determines where to fetch the body from, based on the ContentType - switch (state.getContentType()) { - case MediaType.TEXT_PLAIN: - case MediaType.APPLICATION_JSON: - case MediaType.APPLICATION_XML: - case MediaType.TEXT_HTML: - case MediaType.APPLICATION_OCTET_STREAM: - // Saves the body in case of raw content, or the file location in case of binary - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveBody").toString())); - statement.setInt(1, requestID); - statement.setString(2, state.getBody()); - statement.executeUpdate(); - break; - case MediaType.APPLICATION_FORM_URLENCODED: - if (state.getStringTuples().size() > 0) { - for (Entry entry : state.getStringTuples().entrySet()) { - // Saves the string tuples - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); - statement.setInt(1, requestID); - statement.setString(2, "String"); - statement.setString(3, entry.getKey()); - statement.setString(4, entry.getValue()); - - statement.executeUpdate(); - } - } - break; - case MediaType.MULTIPART_FORM_DATA: - if (state.getStringTuples().size() > 0) { - for (Entry entry : state.getStringTuples().entrySet()) { - // Saves the string tuples - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); - statement.setInt(1, requestID); - statement.setString(2, "String"); - statement.setString(3, entry.getKey()); - statement.setString(4, entry.getValue()); - - statement.executeUpdate(); - } - } - - if (state.getFileTuples().size() > 0) { - for (Entry entry : state.getFileTuples().entrySet()) { - // Saves the file tuples - statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); - statement.setInt(1, requestID); - statement.setString(2, "File"); - statement.setString(3, entry.getKey()); - statement.setString(4, entry.getValue()); - - statement.executeUpdate(); - } - } - break; - } - } - } catch (SQLException e) { - Services.loggingService.logWarning("Database error.", e, LocalDateTime.now()); - } - }, "History Saver Thread").start(); + historySaver.state = state; + Services.singleExecutor.execute(historySaver); // Appends this history item to the HistoryTab Services.homeWindowController.addHistoryItem(state); @@ -431,4 +333,122 @@ public class HistoryManager { } return true; } + + private class HistorySaver implements Runnable { + private DashboardState state; + + @Override + public void run() { + try { + statement = + conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequest").toString())); + + statement.setString(1, state.getHttpMethod()); + statement.setString(2, String.valueOf(state.getTarget())); + statement.setString(3, LocalDate.now().toString()); + + statement.executeUpdate(); + + // Get latest RequestID to insert into Headers table + statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests"); + + ResultSet RS = statement.executeQuery(); + int requestID = -1; + if (RS.next()) + requestID = RS.getInt("MaxID"); + + if (state.getHeaders().size() > 0) { + // Saves request headers + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveHeader").toString())); + for (Entry entry : state.getHeaders().entrySet()) { + statement.setInt(1, requestID); + statement.setString(2, entry.getKey().toString()); + statement.setString(3, entry.getValue().toString()); + + statement.executeUpdate(); + } + } + + if (state.getParams().size() > 0) { + // Saves request parameters + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); + for (Entry entry : state.getParams().entrySet()) { + statement.setInt(1, requestID); + statement.setString(2, "Param"); + statement.setString(3, entry.getKey().toString()); + statement.setString(4, entry.getValue().toString()); + + statement.executeUpdate(); + } + } + + if (!(state.getHttpMethod().equals("GET") || state.getHttpMethod().equals("DELETE"))) { + // Maps the request to its ContentType for faster recovery + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequestContentPair").toString())); + statement.setInt(1, requestID); + statement.setString(2, state.getContentType()); + + statement.executeUpdate(); + + // Determines where to fetch the body from, based on the ContentType + switch (state.getContentType()) { + case MediaType.TEXT_PLAIN: + case MediaType.APPLICATION_JSON: + case MediaType.APPLICATION_XML: + case MediaType.TEXT_HTML: + case MediaType.APPLICATION_OCTET_STREAM: + // Saves the body in case of raw content, or the file location in case of binary + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveBody").toString())); + statement.setInt(1, requestID); + statement.setString(2, state.getBody()); + statement.executeUpdate(); + break; + case MediaType.APPLICATION_FORM_URLENCODED: + if (state.getStringTuples().size() > 0) { + for (Entry entry : state.getStringTuples().entrySet()) { + // Saves the string tuples + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); + statement.setInt(1, requestID); + statement.setString(2, "String"); + statement.setString(3, entry.getKey()); + statement.setString(4, entry.getValue()); + + statement.executeUpdate(); + } + } + break; + case MediaType.MULTIPART_FORM_DATA: + if (state.getStringTuples().size() > 0) { + for (Entry entry : state.getStringTuples().entrySet()) { + // Saves the string tuples + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); + statement.setInt(1, requestID); + statement.setString(2, "String"); + statement.setString(3, entry.getKey()); + statement.setString(4, entry.getValue()); + + statement.executeUpdate(); + } + } + + if (state.getFileTuples().size() > 0) { + for (Entry entry : state.getFileTuples().entrySet()) { + // Saves the file tuples + statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); + statement.setInt(1, requestID); + statement.setString(2, "File"); + statement.setString(3, entry.getKey()); + statement.setString(4, entry.getValue()); + + statement.executeUpdate(); + } + } + break; + } + } + } catch (SQLException e) { + Services.loggingService.logWarning("Database error.", e, LocalDateTime.now()); + } + } + } } diff --git a/src/main/java/com/rohitawate/everest/util/logging/LoggingService.java b/src/main/java/com/rohitawate/everest/util/logging/LoggingService.java index 1d7dd77..84f582c 100644 --- a/src/main/java/com/rohitawate/everest/util/logging/LoggingService.java +++ b/src/main/java/com/rohitawate/everest/util/logging/LoggingService.java @@ -16,10 +16,10 @@ package com.rohitawate.everest.util.logging; +import com.rohitawate.everest.util.Services; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; public class LoggingService { private Logger logger; @@ -27,7 +27,6 @@ public class LoggingService { private String message; private Exception exception; private LocalDateTime time; - private ExecutorService executorService; private SevereLogger severeLogger = new SevereLogger(); private WarningLogger warningLogger = new WarningLogger(); @@ -36,22 +35,21 @@ public class LoggingService { public LoggingService(Level writerLevel) { logger = new Logger(writerLevel); dateFormat = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); - executorService = Executors.newSingleThreadExecutor(); } public void logSevere(String message, Exception exception, LocalDateTime time) { setValues(message, exception, time); - executorService.execute(severeLogger); + Services.singleExecutor.execute(severeLogger); } public void logWarning(String message, Exception exception, LocalDateTime time) { setValues(message, exception, time); - executorService.execute(warningLogger); + Services.singleExecutor.execute(warningLogger); } public void logInfo(String message, LocalDateTime time) { setValues(message, null, time); - executorService.execute(infoLogger); + Services.singleExecutor.execute(infoLogger); } private void setValues(String message, Exception exception, LocalDateTime time) { @@ -60,11 +58,6 @@ public class LoggingService { this.time = time; } - @Override - protected void finalize() { - executorService.shutdown(); - } - private class SevereLogger implements Runnable { @Override public void run() {