Class PluginService

java.lang.Object
ai.nervemind.app.service.PluginService
All Implemented Interfaces:
ai.nervemind.common.service.PluginServiceInterface

@Service public class PluginService extends Object implements ai.nervemind.common.service.PluginServiceInterface
Spring-managed service that discovers and manages plugins using Java's ServiceLoader mechanism.

This service is the core implementation of the NerveMind plugin system. It handles the complete plugin lifecycle from discovery to enabling/disabling at runtime.

Plugin Discovery Sources

Plugins are discovered from two sources during application startup:

  1. Classpath plugins - Bundled JAR dependencies discovered via ServiceLoader. These are declared in build.gradle as runtime dependencies.
  2. External plugins - JAR files placed in the plugins/ directory, loaded dynamically via PluginLoader.

Architecture

                         ┌──────────────────────────────────┐
                         │         PluginService            │
                         │   (Spring @Service singleton)    │
                         └──────────────────────────────────┘
                                        │
             ┌──────────────────────────┼──────────────────────────┐
             │                          │                          │
             ▼                          ▼                          ▼
  ┌────────────────────┐    ┌────────────────────┐    ┌────────────────────┐
  │   ServiceLoader    │    │    PluginLoader    │    │  SettingsService   │
  │ (Classpath JARs)   │    │ (External JARs)    │    │   (Persistence)    │
  └────────────────────┘    └────────────────────┘    └────────────────────┘

Plugin State Management

The service maintains two categories of plugin registries:

  • Discovered plugins (discoveredTriggers, discoveredActions) - All plugins found during discovery, regardless of enabled state.
  • Enabled plugins (enabledTriggers, enabledActions) - Only plugins that are currently enabled and available for use.

Thread Safety

This service uses ConcurrentHashMap for all plugin registries to support safe concurrent access from HTTP request threads, workflow execution threads, and the UI thread.

Usage Example

@Autowired
private PluginService pluginService;

// Get all available (enabled) triggers
Collection<TriggerProvider> triggers = pluginService.getTriggerProviders();

// Get a specific trigger's executor for workflow execution
Optional<NodeExecutor> executor = pluginService.getExecutor("ai.nervemind.plugin.filewatcher");
executor.ifPresent(exec -> exec.execute(context));
Since:
1.0.0
See Also:
  • Contract interface in common module
  • External JAR loading component
  • Interface for trigger plugins
  • Interface for action plugins
  • UI for managing plugins
  • Constructor Details

    • PluginService

      public PluginService(ai.nervemind.common.service.SettingsServiceInterface settingsService, PluginLoader pluginLoader)
      Constructs the PluginService with required dependencies.

      This constructor is called by Spring's dependency injection. The actual plugin discovery occurs in discoverPlugins() which is annotated with PostConstruct.

      Parameters:
      settingsService - service for persisting plugin enabled states
      pluginLoader - loader for external plugin JARs
  • Method Details

    • discoverPlugins

      @PostConstruct public void discoverPlugins()
      Discovers and registers all available plugins at application startup.

      This method is automatically called by Spring after dependency injection via the PostConstruct annotation. It performs a complete plugin discovery from all sources.

      Discovery Order

      1. Classpath plugins - Bundled plugins are discovered first
      2. External plugins - Plugins from the plugins/ directory are loaded and may override classpath versions
      3. Enable state - Previously enabled plugins are restored from settings

      After this method completes, use getAllDiscoveredPlugins() to get the complete list of available plugins, or getTriggerProviders() and getActionProviders() to get only enabled plugins.

      See Also:
    • getAllDiscoveredPlugins

      public List<ai.nervemind.common.service.PluginServiceInterface.PluginInfo> getAllDiscoveredPlugins()
      Specified by:
      getAllDiscoveredPlugins in interface ai.nervemind.common.service.PluginServiceInterface
    • isPluginEnabled

      public boolean isPluginEnabled(String pluginId)
      Check if a plugin is enabled.
      Specified by:
      isPluginEnabled in interface ai.nervemind.common.service.PluginServiceInterface
    • enablePlugin

      public void enablePlugin(String pluginId)
      Enable a plugin.
      Specified by:
      enablePlugin in interface ai.nervemind.common.service.PluginServiceInterface
    • disablePlugin

      public void disablePlugin(String pluginId)
      Disable a plugin.
      Specified by:
      disablePlugin in interface ai.nervemind.common.service.PluginServiceInterface
    • setPluginEnabled

      public void setPluginEnabled(String pluginId, boolean enabled)
      Set enabled state for a plugin.
      Specified by:
      setPluginEnabled in interface ai.nervemind.common.service.PluginServiceInterface
    • getTriggerProviders

      public Collection<ai.nervemind.plugin.api.TriggerProvider> getTriggerProviders()
      Gets all enabled trigger providers.
      Returns:
      an unmodifiable collection of enabled trigger providers
    • getActionProviders

      public Collection<ai.nervemind.plugin.api.ActionProvider> getActionProviders()
      Gets all enabled action providers.
      Returns:
      an unmodifiable collection of enabled action providers
    • getAllNodes

      public Collection<ai.nervemind.plugin.api.NodeDescriptor> getAllNodes()
      Gets all enabled node descriptors.
      Returns:
      an unmodifiable collection of enabled node descriptors
    • getTrigger

      public Optional<ai.nervemind.plugin.api.TriggerProvider> getTrigger(String nodeType)
      Gets the trigger provider for the specified node type.
      Parameters:
      nodeType - the node type to look up
      Returns:
      an Optional containing the trigger provider if found
    • getAction

      public Optional<ai.nervemind.plugin.api.ActionProvider> getAction(String nodeType)
      Gets the action provider for the specified node type.
      Parameters:
      nodeType - the node type to look up
      Returns:
      an Optional containing the action provider if found
    • getNode

      public Optional<ai.nervemind.plugin.api.NodeDescriptor> getNode(String nodeType)
      Gets the node descriptor for the specified node type.
      Parameters:
      nodeType - the node type to look up
      Returns:
      an Optional containing the node descriptor if found
    • getExecutor

      public Optional<ai.nervemind.plugin.api.NodeExecutor> getExecutor(String nodeType)
      Gets the executor for the specified node type.
      Parameters:
      nodeType - the node type to look up
      Returns:
      an Optional containing the node executor if found
    • getProperties

      public List<ai.nervemind.plugin.api.PropertyDefinition> getProperties(String nodeType)
      Gets the property definitions for the specified node type.
      Parameters:
      nodeType - the node type to look up
      Returns:
      a list of property definitions for the node type
    • requiresBackgroundService

      public boolean requiresBackgroundService(String nodeType)
      Checks if the specified node type requires a background service.
      Parameters:
      nodeType - the node type to check
      Returns:
      true if the node type requires a background service
    • getNodesByCategory

      public Map<ai.nervemind.plugin.api.NodeCategory, List<ai.nervemind.plugin.api.NodeDescriptor>> getNodesByCategory()
      Gets all node descriptors grouped by category.
      Returns:
      a map of node categories to lists of node descriptors
    • reloadPlugins

      public void reloadPlugins()
      Reload all plugins (classpath + external JARs). Useful after adding new plugin JARs to the plugins directory.
    • getLoadedPluginJars

      public List<PluginLoader.PluginJarInfo> getLoadedPluginJars()
      Get information about loaded plugin JARs.
      Returns:
      a list of information about loaded plugin JARs
    • getPluginsDirectory

      public String getPluginsDirectory()
      Get the plugins directory path.
      Returns:
      the path to the plugins directory
    • getPluginProviders

      public Collection<ai.nervemind.plugin.api.PluginProvider> getPluginProviders()
      Get all enabled PluginProvider implementations.
      Returns:
      unmodifiable collection of enabled plugin providers
    • getPluginProvider

      public Optional<ai.nervemind.plugin.api.PluginProvider> getPluginProvider(String nodeType)
      Get a specific PluginProvider by node type.
      Parameters:
      nodeType - the node type identifier
      Returns:
      Optional containing the provider, or empty if not found
    • getPluginProviderInstance

      public Object getPluginProviderInstance(String pluginId)
      Specified by:
      getPluginProviderInstance in interface ai.nervemind.common.service.PluginServiceInterface
    • getHandleDefinitions

      public List<ai.nervemind.common.service.PluginServiceInterface.HandleInfo> getHandleDefinitions(String nodeType)
      Gets the handle definitions for a node type.

      If the node type is from a PluginProvider, returns its custom handle definitions. Otherwise, returns the default handles (1 input left, 1 output right).

      Specified by:
      getHandleDefinitions in interface ai.nervemind.common.service.PluginServiceInterface
      Parameters:
      nodeType - the node type identifier
      Returns:
      list of handle info records
    • getCustomNodeView

      public Object getCustomNodeView(String nodeType, String nodeId, String displayName, Map<String,Object> settings, boolean selected, boolean executing, boolean error)
      Gets a custom node view from a PluginProvider, if available.
      Specified by:
      getCustomNodeView in interface ai.nervemind.common.service.PluginServiceInterface
      Parameters:
      nodeType - the node type identifier
      nodeId - the instance node ID
      displayName - the display name of the node
      settings - the node's current settings
      selected - whether the node is selected
      executing - whether the node is executing
      error - whether the node has an error
      Returns:
      the custom JavaFX node, or null to use default rendering
    • getAllMenuContributions

      public List<ai.nervemind.common.service.PluginServiceInterface.MenuContribution> getAllMenuContributions()
      Gets all menu contributions from enabled plugins.

      Aggregates menu contributions from all currently enabled PluginProvider implementations. Each contribution is tagged with the source plugin ID.

      Specified by:
      getAllMenuContributions in interface ai.nervemind.common.service.PluginServiceInterface
      Returns:
      list of menu contributions from all enabled plugins
    • getAllSidePanelContributions

      public List<ai.nervemind.common.service.PluginServiceInterface.SidePanelContribution> getAllSidePanelContributions()
      Gets all side panel contributions from enabled plugins.

      Aggregates side panel contributions from all currently enabled PluginProvider implementations. Each contribution is tagged with the source plugin ID.

      Specified by:
      getAllSidePanelContributions in interface ai.nervemind.common.service.PluginServiceInterface
      Returns:
      list of side panel contributions from all enabled plugins
    • notifyConnectionCreated

      public void notifyConnectionCreated(Object connectionContext)
      Specified by:
      notifyConnectionCreated in interface ai.nervemind.common.service.PluginServiceInterface