{"id":36427030,"url":"https://github.com/fathzer/plugin-loader","last_synced_at":"2026-01-11T18:01:24.106Z","repository":{"id":65507605,"uuid":"574422817","full_name":"fathzer/plugin-loader","owner":"fathzer","description":"A java plugin loader","archived":false,"fork":false,"pushed_at":"2023-05-09T17:14:09.000Z","size":217,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-11T21:49:16.980Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fathzer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-05T09:25:56.000Z","updated_at":"2022-12-06T14:41:50.000Z","dependencies_parsed_at":"2023-02-14T08:30:56.255Z","dependency_job_id":null,"html_url":"https://github.com/fathzer/plugin-loader","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/fathzer/plugin-loader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fathzer%2Fplugin-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fathzer%2Fplugin-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fathzer%2Fplugin-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fathzer%2Fplugin-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fathzer","download_url":"https://codeload.github.com/fathzer/plugin-loader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fathzer%2Fplugin-loader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28316942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T14:58:17.114Z","status":"ssl_error","status_checked_at":"2026-01-11T14:55:53.580Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-11T18:00:58.052Z","updated_at":"2026-01-11T18:01:24.070Z","avatar_url":"https://github.com/fathzer.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Maven Central](https://img.shields.io/maven-central/v/com.fathzer/plugin-loader)](https://central.sonatype.com/artifact/com.fathzer/plugin-loader)\n[![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](https://github.com/fathzer/plugin-loader/blob/master/LICENSE)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=fathzer_plugin-loader\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=fathzer_plugin-loader)\n[![javadoc](https://javadoc.io/badge2/com.fathzer/plugin-loader/javadoc.svg)](https://javadoc.io/doc/com.fathzer/plugin-loader)\n\n# plugin-loader\nA java plugin loader.\n\n## Table of contents\n- [Introduction](#introduction)\n- [Requirements](#requirements)\n- [How to load plugins from jar files](#how-to-load-plugins-from-jar-files)\n- [How to load plugins from ClassLoader](#how-to-load-plugins-from-classloader)\n- [Working with custom plugins](#working-with-custom-plugins)\n- [A word about error management](#a-word-about-error-management)\n- [Advanced usage](#advanced-usage)\n  - [Plugin registry](#plugin-registry)\n  - [Download plugins from a repository](#download-plugins-from-a-repository)\n\n## Introduction\nA plugin is a class that is loaded dynamically by an application to give extra functionalities or customization.\n\nFrom the technical point of view, the plugin should implement an interface (or extends a class) defined by the application.  \nUsually, the plugin can be stored in a jar file, but you can imagine loading it from the network.\n\nThis library helps application developper's to manage plugins in their application.  \nIt provides an abstraction of the process of loading plugins and concrete implementations to load plugins stored in jar files.  \nUnlike [java.util.ServiceLoader](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html), it allows to customize error management and how plugin classes are discovered and instantiated.\n\nThe [plugin-loader-example](https://github.com/fathzer/plugin-loader/tree/main/plugin-loader-example) folder contains an example of jar plugin implementation and loading.\n\n## Requirements\nIt requires java 11+.  \nNevertheless, a variant of this library is available for Java 8 users. They have to use the 'jdk8' [maven classifier](https://www.baeldung.com/maven-artifact-classifiers#bd-3-consuming-jar-artifact-of-a-specific-java-version) in their dependency. Only the [com.fathzer.plugin.loader.utils.AbstractPluginDownloader class](#download-plugins-from-a-repository) is not available in this variant.\n\n## How to load plugins from jar files\n\n### First define an interface for your plugin.\n\nIn the example, it is a very basic interface, but it can be as complex as you need. It also can be an abstract class.  \nIt's a good practice to define this interface in a library different from the application it self, in order to clearly define the dependencies between application and its plugins.  \nHere is the example:\n\n```java\npackage com.myapp;\npublic interface AppPlugin {\n    String getGreeting();\n}\n```\n\nUsually there's also an implicit contract about how to instantiate the plugin. The default is to use the no arguments constructor.\n\n### Then, implement the plugin.\nHere is the code of the example plugin:\n\n```java\npublic class MyPlugin implements AppPlugin {\n    @Override\n    public String getGreeting() {\n        return \"Hello, I'm a plugin\";\n    }\n}\n```\n\nYou should package the class in a jar file.  \nAs the plugin can be complex, the jar file can contains many classes. So, you have to define which class implements the plugin interface. The standard way is to add a resource file in META-INF/services. In this example, its path should be META-INF/services/com.myapp.AppPlugin and it should contain the canonical name of every plugin implementation classes (see [ServiceLoader documentation](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) to have the exact format of the file).  \n\n### Finally load the plugin in your application\n\ncom.fathzer.plugin.loader.jar.JarPluginLoader will allow you to get the plugin in your application.\n\nHere is an example that loads the plugins contained in the *pluginFile* local jar file:\n\n```java\nfinal PluginLoader\u003cPath\u003e loader = new JarPluginLoader();\nfinal List\u003cAppPlugin\u003e plugins = loader.getPlugins(pluginFile, AppPlugin.class);\n```\n\nAn usual need is to load all plugins in a local directory.  \n*com.fathzer.loader.utils.FileUtils.getJarFiles* method allows you to search for jar files in a directory.  \nYou have then to iterate over the returned files.\n\n## How to load plugins from ClassLoader\nJarPluginLoader is not the only way to load plugins. *com.fathzer.plugin.loader.PluginLoader* is an abstract class that can have multiple implementations.  \nAnother classical implementation provided by this library is *ClassLoaderPluginLoader*.  \nIt allows you to search and load plugins through a class loader.\n\nA typical use is to load the plugins available on the classpath. Here is the code to do that:\n```java\nfinal ClassLoaderPluginLoader loader = new ClassLoaderPluginLoader();\nfinal List\u003cAppPlugin\u003e plugins = loader.getPlugins(AppPlugin.class);\n```\n\n## Working with custom plugins\nThe default implementation works with plugin defined as services useable with [ServiceLoader documentation](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html), but you can change how the plugins are discovered or how they are instantiated.\n\nThe plugin class names are discovered by a ClassNameBuilder. You can change the default one using the PluginLoader.withClassNameBuilder.  \nHere is an example that use a predefined class name:\n```java\nfinal PluginLoader\u003cClassLoader\u003e loader = new ClassLoaderPluginLoader();\nloader.withClassNameBuilder((c,v) -\u003e Collections.singleton(\"com.fathzer.MyPlugin\"));\n```\n\nYou can also change the way plugins are instantiated using the PluginLoader.withInstanceBuilder method. For instance to use a constructor with argument.  \nHere is an example that uses a constructor with a fixed string argument:\n```java\nfinal PluginLoader\u003cClassLoader\u003e loader = new ClassLoaderPluginLoader();\nfinal String context = ...\nInstanceBuilder ib = new InstanceBuilder() {\n  @Override\n  public \u003cT\u003e T get(Class\u003cT\u003e pluginClass) throws Exception {\n    final Constructor\u003cT\u003e constructor = pluginClass.getConstructor(String.class);\n    return constructor.newInstance(context);\n  }\n};\nloader.withInstanceBuilder(ib);\n```\n\n## A word about error management\nIf a problem occurs during plugin instantiation, a *PluginInstantiationException* is throw. This is the default behaviour, but you prefer to log the error and continue to instantiate other plugins contained in a jar.  \nYou can simply customize the exception management using the *PluginLoader.withExceptionConsumer* method as in the following example:\n```java\nnew ClassLoaderPluginLoader().withExceptionConsumer(e -\u003e log.warn(\"An error occurred while loading plugins\", e));\n```\n\n## Advanced usage\nAn usual need is to have a bunch of plugins that are selected by a key. For instance, you can imagine an interface that do *something* with an URI. You can have multiple implementations of the interface, one for each supported uri scheme.\n\n### Plugin registry\nThe *com.fathzer.loader.utils.PluginRegistry* maps String keys to plugin instantiations. You can register plugins, then retrieve them from their keys, , etc...\n\n### Download plugins from a repository\n**Warning: This section is not available for java8 version of this library**.\n\nOnce you have a plugin registry, a basic way to populate it is to read plugins from a local jar directory.  \nIt's simple ... but how to update this local directory when a new plugin is released?  \n\nThe *com.fathzer.plugin.loader.utils.AbtractPluginDownloader* class allows you to define your own plugin remote repository and download the required jar to a local folder.  \nYou can then use JarPluginLoader to load these plugins.\n\nA repository is basically a map between a key and an URI where to download a jar.  \nThis map should be available at an URI. Let's say for instance *https://com.myApp/AppPluginRepository*.  \nThe format of serialization format of the map is free. The AbtractPluginDownloader is abstract because you have to implement the parsing of the repository URI. For the example, let say the map is stored in json format:\n```json\n{\n  \"https\":\"https://com.myApp/AppPlugins/http.jar\",\n  \"sftp\":\"https://com.myApp/AppPlugins/sftp.jar\"\n}\n```\n\nHere is an implementation that uses jackson to parse the map:\n```java\nAbstractPluginsDownloader dl = new AbstractPluginsDownloader(URI.create(\"https://com.myApp/AppPluginRepository\"), Paths.get(\"Plugins\")) {\n  @Override\n  protected Map\u003cString, URI\u003e getURIMap(InputStream in) throws IOException {\n    return new ObjectMapper().readValue(in, new TypeReference\u003cHashMap\u003cString, URI\u003e\u003e() {});\n  }\n};\n```\n\nYou can then load the URI map using ```dl.getURIMap()```, or download jar plugins for some keys using ``̀ dl.download(\"sftp\")```.\n\nAbstractPluginsDownloader has many protected methods. Feel free to override them to make this class fits with your needs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffathzer%2Fplugin-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffathzer%2Fplugin-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffathzer%2Fplugin-loader/lists"}