diff --git a/README.md b/README.md index 87b68e9..dc67f8d 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,22 @@ In situations where specific hosts lack internet access, this tool enables the t ## Required Components 1. Java 8 or above +## Features +1. Downloads plugins and their associated dependent libraries to local storage. +2. Stores all information in an SQLITE DB. +3. Provides a UI to upload custom plugins restricted to sharing within the organization. +4. Creates and exposes a modified API to retrieve plugin info for JMeter Plugin Manager. +5. Easily configurable scheduler to check for newly available plugins/versions in the market. + +## Available APIs + +| Service | HTTP Method | URI | +|:----------------------|:-----------:|:------------------------------------------| +| App Running Status | GET | http://:\/v1/status | +| Get Plugins | GET | http://:\/v1/plugins | +| Upload Custom Plugin | GET | http://:\/v1/upload | +| View Plugins in table | GET | http://:\/v1/dashboard | + ## Creating Properties file create a properties file with the below contents. @@ -26,22 +42,22 @@ db.timeout.secs=300 jmeter.plugins.url=https://jmeter-plugins.org/repo/ mvn.repo.url=https://mvnrepository.com/search?q= +# DB Connection Pool configuration +db.min.threads=2 +db.max.threads=10 +db.timeout.secs=300 + +# External APIs +jmeter.plugins.url=https://jmeter-plugins.org/repo/ +mvn.repo.url=https://mvnrepository.com/search?q= + # Directory Configs local.repo.path=/app/plugins-manager/ # Uncomment the below if you are running this on Windows -local.repo.path=C:\\Temp\\plugins-manager\\ +# local.repo.path=C:\\Temp\\plugins-manager\\ ``` - -## Available APIs - -| Service | HTTP Method | URI | -|:-------------------|:-----------:|:----------------------------------------| -| App Running Status | GET | http://:\/v1/ | -| Get Plugins | GET | http://:\/v1/plugins | -| Upload Custom Plugin | GET | http://:\/v1/upload | - - + ## How to Set up * Download the Latest release from [here](https://github.com/rollno748/Jmeter-local-plugins-manager/tags) @@ -49,16 +65,10 @@ local.repo.path=C:\\Temp\\plugins-manager\\ * Run the JAR (java -jar jmeter-local-plugins-manager-2.0.jar -c configuration.properties) * Go to the JMeter installed directory and set `jpgc.repo.address` (this should be the local plugins manager API) in the jmeter.properties -## Features -1. Downloads plugins and their associated dependent libraries to local storage. -2. Stores all information in an SQLITE DB. -3. Provides a UI to upload custom plugins restricted to sharing within the organization. -4. Creates and exposes a modified API to retrieve plugin info for JMeter Plugin Manager. -5. Easily configurable scheduler to check for newly available plugins/versions in the market. - ## Uploading Custom plugin ![Custom Upload Form](/img/upload-form.jpg) + ## How it works ? * Acts as an independent server that polls the plugins manager for updates (configurable). @@ -69,6 +79,11 @@ local.repo.path=C:\\Temp\\plugins-manager\\ - Custom Plugins API - Merged (Public and Custom) Plugins API +## Detailed Instructions + +* Detailed instructions is available at [Medium](https://rollno748.medium.com/creating-your-own-jmeter-plugin-manager-a-comprehensive-guide-aaa57021dda9) + + ## Tools used - Spark java framework - Sqlite DB @@ -83,4 +98,4 @@ If this project help you reduce time to develop, you can give me a cup of coffee Please rate a star(:star2:) - If you like it. Please open up a bug(:beetle:) - If you experience abnormalities. - + diff --git a/pom.xml b/pom.xml index c787365..9d03204 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.perfwise.local.pluginsmanager jmeter-local-plugins-manager - 2.0 + 2.1 2.9.4 @@ -13,7 +13,7 @@ 4.5.14 20230618 2.8.9 - 2.12.5 + 2.12.7.1 1.16.1 1.2.6 1.5 diff --git a/src/main/java/io/perfwise/local/pluginsmanager/controller/RestController.java b/src/main/java/io/perfwise/local/pluginsmanager/controller/RestController.java index 15ef33b..de1f053 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/controller/RestController.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/controller/RestController.java @@ -107,6 +107,12 @@ public void loadRestApiServices() { ""; }); + get("/dashboard", (req, res) -> { + res.type("text/html"); + res.redirect("/dashboard.html"); + return null; + }); + get("/upload", (req, res) -> { res.type("text/html"); res.redirect("/"); @@ -129,6 +135,13 @@ public void loadRestApiServices() { } }); + get("/plugins-table", (req, res) -> { + res.type("application/json"); + String type = req.queryParams("type"); + PluginService pluginService = new PluginServiceImpl(); + return pluginService.getPluginTable(); + }); + post("/upload", (req, res) -> { req.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp")); UploadService uploadService = new UploadServiceImpl(this.customPluginsPath, this.libPath); diff --git a/src/main/java/io/perfwise/local/pluginsmanager/model/MetadataModel.java b/src/main/java/io/perfwise/local/pluginsmanager/model/MetadataModel.java index 54037fe..85f7cbd 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/model/MetadataModel.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/model/MetadataModel.java @@ -1,8 +1,6 @@ package io.perfwise.local.pluginsmanager.model; - import com.fasterxml.jackson.annotation.JsonProperty; -import org.json.JSONObject; public class MetadataModel { @JsonProperty("id") diff --git a/src/main/java/io/perfwise/local/pluginsmanager/model/Plugin.java b/src/main/java/io/perfwise/local/pluginsmanager/model/Plugin.java deleted file mode 100644 index 987ed67..0000000 --- a/src/main/java/io/perfwise/local/pluginsmanager/model/Plugin.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.perfwise.local.pluginsmanager.model; - -public class Plugin { - private String id; - private String name; - private String description; - private String helpUrl; - private String screenshotUlr; - private String vendor ; - private String version; - private String pluginPath; - private String libPath; - - public Plugin(String id, String name, String description, String helpUrl, String screenshotUlr, String vendor, String version, String pluginPath, String libPath) { - this.id = id; - this.name = name; - this.description = description; - this.helpUrl = helpUrl; - this.screenshotUlr = screenshotUlr; - this.vendor = vendor; - this.version = version; - this.pluginPath = pluginPath; - this.libPath = libPath; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getHelpUrl() { - return helpUrl; - } - - public void setHelpUrl(String helpUrl) { - this.helpUrl = helpUrl; - } - - public String getScreenshotUlr() { - return screenshotUlr; - } - - public void setScreenshotUlr(String screenshotUlr) { - this.screenshotUlr = screenshotUlr; - } - - public String getVendor() { - return vendor; - } - - public void setVendor(String vendor) { - this.vendor = vendor; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getPluginPath() { - return pluginPath; - } - - public void setPluginPath(String pluginPath) { - this.pluginPath = pluginPath; - } - - public String getLibPath() { - return libPath; - } - - public void setLibPath(String libPath) { - this.libPath = libPath; - } -} diff --git a/src/main/java/io/perfwise/local/pluginsmanager/scheduler/http/HttpRequest.java b/src/main/java/io/perfwise/local/pluginsmanager/scheduler/http/HttpRequest.java index 1a6616c..5f3d27d 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/scheduler/http/HttpRequest.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/scheduler/http/HttpRequest.java @@ -52,6 +52,7 @@ public class HttpRequest { private static final String INSERT_PLUGIN_INFO = "INSERT INTO plugins (id, name, type, description, helpUrl, markerClass, screenshotUrl, vendor, versions_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; private static final String SELECT_PLUGIN_VERSION_METADATA = "SELECT COUNT(*) AS COUNT FROM METADATA WHERE ID = ? AND VERSION = ?"; private static final String SELECT_PLUGINS = "SELECT ID, NAME, DESCRIPTION, HELPURL, MARKERCLASS, SCREENSHOTURL, VENDOR FROM plugins"; + private static final String SELECT_PLUGINS_TABLE_DATA = "SELECT ID, NAME, TYPE, DESCRIPTION, HELPURL, MARKERCLASS, SCREENSHOTURL, VENDOR, VERSIONS_COUNT FROM plugins"; private static final String SELECT_PLUGINS_WITH_FILTER = "SELECT ID, NAME, DESCRIPTION, HELPURL, MARKERCLASS, SCREENSHOTURL, VENDOR FROM plugins WHERE type = ?"; private static final String SELECT_METADATA_BY_ID = "SELECT ID, VERSION, DOWNLOADURL, LIBS FROM metadata WHERE ID = ?"; @@ -226,6 +227,43 @@ public JSONArray getCustomPlugins() { return fetchPluginsFromLocalDB(SELECT_PLUGINS_WITH_FILTER, "custom"); } + public JSONArray getAllPluginsTableData() { + return fetchAllPluginsTableData(); + } + + private JSONArray fetchAllPluginsTableData() { + JSONArray jsonArray = new JSONArray(); + try{ + if(conn == null || conn.isClosed()){ + conn = SQLiteConnectionPool.getConnection(); + } + PreparedStatement preparedStatement = conn.prepareStatement(HttpRequest.SELECT_PLUGINS_TABLE_DATA); + ResultSet rs = preparedStatement.executeQuery(); + + while (rs.next()) { + JSONObject pluginObject = new JSONObject(); + JSONObject libraryObj; + + pluginObject.put("id", rs.getString("id")); + pluginObject.put("name", rs.getString("name")); + pluginObject.put("type", rs.getString("type")); + pluginObject.put("description", rs.getString("description")); + pluginObject.put("helpUrl", rs.getString("helpUrl")); + pluginObject.put("markerClass", rs.getString("markerClass")); + pluginObject.put("screenshotUrl", rs.getString("screenshotUrl")); + pluginObject.put("vendor", rs.getString("vendor")); + pluginObject.put("versions_count", rs.getString("versions_count")); + jsonArray.put(pluginObject); + } + preparedStatement.close(); + }catch(SQLException | InterruptedException e){ + LOGGER.error("Exception occurred while fetching plugins information"); + } finally { + SQLiteConnectionPool.releaseConnection(conn); + } + return jsonArray; + } + private JSONObject getDependentLibraryObj(String id, String type) throws UnknownHostException { String host = InetAddress.getLocalHost().getHostAddress(); @@ -404,5 +442,4 @@ public Properties getProps() { public void setProps(Properties props) { this.props = props; } - } diff --git a/src/main/java/io/perfwise/local/pluginsmanager/scheduler/parser/Parse.java b/src/main/java/io/perfwise/local/pluginsmanager/scheduler/parser/Parse.java index 155b460..3bd27c6 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/scheduler/parser/Parse.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/scheduler/parser/Parse.java @@ -120,6 +120,10 @@ public static JSONArray getCustomPlugins() { return httpRequest.getCustomPlugins(); } + public static JSONArray getPluginsTableData() { + return httpRequest.getAllPluginsTableData(); + } + public void downloadMissingPlugins(JSONArray missingPluginsList) throws URISyntaxException, IOException { for (int i = 0; i < missingPluginsList.length(); i++) { httpRequest.downloadMissingPlugins(missingPluginsList.getJSONObject(i)); diff --git a/src/main/java/io/perfwise/local/pluginsmanager/service/PluginService.java b/src/main/java/io/perfwise/local/pluginsmanager/service/PluginService.java index 69f6b5c..acebc29 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/service/PluginService.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/service/PluginService.java @@ -6,4 +6,5 @@ public interface PluginService { JSONArray getAllPlugins(); JSONArray getPublicPlugins(); JSONArray getCustomPlugins(); + JSONArray getPluginTable(); } diff --git a/src/main/java/io/perfwise/local/pluginsmanager/service/PluginServiceImpl.java b/src/main/java/io/perfwise/local/pluginsmanager/service/PluginServiceImpl.java index 645f5a7..3c7bac6 100644 --- a/src/main/java/io/perfwise/local/pluginsmanager/service/PluginServiceImpl.java +++ b/src/main/java/io/perfwise/local/pluginsmanager/service/PluginServiceImpl.java @@ -19,4 +19,11 @@ public JSONArray getPublicPlugins() { public JSONArray getCustomPlugins() { return Parse.getCustomPlugins(); } + + @Override + public JSONArray getPluginTable() { + return Parse.getPluginsTableData(); + } + + } diff --git a/src/main/resources/public/assets/css/dashboard.css b/src/main/resources/public/assets/css/dashboard.css new file mode 100644 index 0000000..7c69f8e --- /dev/null +++ b/src/main/resources/public/assets/css/dashboard.css @@ -0,0 +1,30 @@ +#tableInput { + background-image: url('./../ico/search.png'); + background-position: 10px 12px; + background-repeat: no-repeat; + width: 97%; + font-size: 16px; + padding: 12px 20px 12px 40px; + border: 1px solid #ddd; + margin-bottom: 12px; +} + +#pluginTable { + border-collapse: collapse; + width: 100%; + border: 1px solid #ddd; + font-size: 18px; +} + +#pluginTable th, #pluginTable td { + text-align: left; + padding: 12px; +} + +#pluginTable tr { + border-bottom: 1px solid #ddd; +} + +#pluginTable tr.header, #pluginTable tr:hover { + background-color: #f1f1f1; +} \ No newline at end of file diff --git a/src/main/resources/public/styles.css b/src/main/resources/public/assets/css/styles.css similarity index 100% rename from src/main/resources/public/styles.css rename to src/main/resources/public/assets/css/styles.css diff --git a/src/main/resources/public/assets/ico/search.png b/src/main/resources/public/assets/ico/search.png new file mode 100644 index 0000000..9d28b05 Binary files /dev/null and b/src/main/resources/public/assets/ico/search.png differ diff --git a/src/main/resources/public/assets/js/dashboard.js b/src/main/resources/public/assets/js/dashboard.js new file mode 100644 index 0000000..5c734ab --- /dev/null +++ b/src/main/resources/public/assets/js/dashboard.js @@ -0,0 +1,67 @@ +function myFunction() { + // Declare variables + var input, filter, table, tr, td, i, txtValue; + input = document.getElementById("tableInput"); + filter = input.value.toUpperCase(); + table = document.getElementById("pluginTable"); + tr = table.getElementsByTagName("tr"); + + for (i = 0; i < tr.length; i++) { + var displayRow = false; // Flag to determine if the row should be displayed + + // Loop through all columns in the current row + for (j = 0; j < tr[i].cells.length; j++) { + td = tr[i].cells[j]; + if (td) { + txtValue = td.textContent || td.innerText; + if (txtValue.toUpperCase().indexOf(filter) > -1) { + displayRow = true; // Set the flag to true if the filter text is found in any column + break; // No need to check other columns, so exit the loop + } + } + } + + // Set the row's display property based on the flag + tr[i].style.display = displayRow ? "" : "none"; + } +} + + +function populateTable(data) { + var table = document.getElementById("pluginTable"); + + for (var i = 0; i < data.length; i++) { + var row = table.insertRow(-1); // Insert a new row at the end of the table + + // Insert cells into the row and populate them with data + var idCell = row.insertCell(0); + var nameCell = row.insertCell(1); + var typeCell = row.insertCell(2); + var vendorCell = row.insertCell(3); + var versionsCountCell = row.insertCell(4); + + + idCell.innerHTML = data[i].id; + nameCell.innerHTML = data[i].name; + typeCell.innerHTML = data[i].type; + vendorCell.innerHTML = data[i].vendor; + versionsCountCell.innerHTML = data[i].versions_count; + } +} + +// Function to fetch data from the API +function fetchDataAndPopulateTable() { + fetch('/v1/plugins-table') + .then(response => response.json()) + .then(data => { + populateTable(data); + }) + .catch(error => { + console.error('Error fetching data:', error); + }); +} + +// Call the function to fetch and populate data when the page loads +window.onload = function () { + fetchDataAndPopulateTable(); +}; \ No newline at end of file diff --git a/src/main/resources/public/script.js b/src/main/resources/public/assets/js/script.js similarity index 100% rename from src/main/resources/public/script.js rename to src/main/resources/public/assets/js/script.js diff --git a/src/main/resources/public/dashboard.html b/src/main/resources/public/dashboard.html new file mode 100644 index 0000000..8146d93 --- /dev/null +++ b/src/main/resources/public/dashboard.html @@ -0,0 +1,39 @@ + + + + Plugins Table + + + + + + + Fork me on GitHub + +
+ +
+
+ + + + + + + + + + +
IDNameTypeVendorVersions Count
+
+ + + + diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html index 3ce40ed..1111a0b 100644 --- a/src/main/resources/public/index.html +++ b/src/main/resources/public/index.html @@ -2,7 +2,7 @@ Custom Plugin Form - + @@ -45,6 +45,6 @@

Custom Plugin Upload Form

- +