Skip to content

Plugin SDK

The MetaonePlugin base class is the entry point for all in-process extensions. It extends PF4J's SpringPlugin and sets up an isolated Spring ApplicationContext inside the plugin, sharing the host's database connection.

MetaonePlugin

java
public abstract class MetaonePlugin extends SpringPlugin {
    protected final Logger log;

    protected MetaonePlugin(PluginWrapper wrapper) { ... }

    /** Called after the Spring plugin context is refreshed. Override for startup logic. */
    protected void onPluginStart() {}

    /** Called before Spring context is closed. Override for cleanup logic. */
    protected void onPluginStop() {}

    /** Return the Spring @Configuration classes to register in the plugin context. */
    protected abstract Class<?>[] getPluginConfigClasses();
}

Plugin Spring Context

Each plugin gets its own isolated AnnotationConfigApplicationContext. The host injects these beans automatically:

BeanTypeDescription
dataSourceDataSourceWorkspace-scoped DataSource (via WorkspaceDataSourceProvider)
eventBusGatewayEventBusGatewayPublish events to the platform
extensionConfigProviderExtensionConfigProviderRead per-workspace config values

You can inject all of these directly in your plugin's Spring beans.

Creating a Plugin

1. Add PF4J manifest entries to pom.xml

xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifestEntries>
        <Plugin-Id>crm-extension</Plugin-Id>
        <Plugin-Version>1.0.0</Plugin-Version>
        <Plugin-Class>vn.metaone.crm.CrmPlugin</Plugin-Class>
        <Plugin-Provider>MetaOne</Plugin-Provider>
        <Plugin-Dependencies></Plugin-Dependencies>
      </manifestEntries>
    </archive>
  </configuration>
</plugin>

2. Implement the Plugin class

java
package vn.metaone.crm;

import org.pf4j.PluginWrapper;
import vn.metaone.sdk.plugin.MetaonePlugin;

public class CrmPlugin extends MetaonePlugin {

    public CrmPlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Override
    protected Class<?>[] getPluginConfigClasses() {
        return new Class<?>[]{ CrmJpaConfig.class, CrmServiceConfig.class };
    }

    @Override
    protected void onPluginStart() {
        log.info("CRM plugin started");
    }

    @Override
    protected void onPluginStop() {
        log.info("CRM plugin stopped");
    }
}

3. Configure JPA

Use a dedicated JPA config that scans only the plugin's own packages:

java
@Configuration
@EnableJpaRepositories(
    basePackages = "vn.metaone.crm.repository",
    entityManagerFactoryRef = "crmEntityManagerFactory",
    transactionManagerRef = "crmTransactionManager"
)
public class CrmJpaConfig {

    @Bean
    public LocalContainerEntityManagerFactoryBean crmEntityManagerFactory(DataSource dataSource) {
        var factory = new LocalContainerEntityManagerFactoryBean();
        factory.setDataSource(dataSource);                 // Injected by MetaonePlugin
        factory.setPackagesToScan("vn.metaone.crm.entity");
        factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        return factory;
    }

    @Bean
    public PlatformTransactionManager crmTransactionManager(
            @Qualifier("crmEntityManagerFactory") EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

Keep entity/repository/service packages inside the plugin's own root package to avoid scan collisions with the host or other plugins.

4. Write Capability Handlers

See the Capability SDK page.

5. Write Event Handlers

See the Event SDK page.

WorkspaceDataSourceProvider

Available in the host context (injected into the plugin automatically). Not typically used directly — MetaonePlugin.createApplicationContext() already registers the workspace DataSource bean.

java
public interface WorkspaceDataSourceProvider {
    DataSource getWorkspaceDataSource();
}

ExtensionConfigProvider

Read per-workspace configuration values stored in ext_config. Inject it in any plugin Spring bean:

java
public interface ExtensionConfigProvider {
    String get(String workspaceId, String extensionKey, String key);
    String get(String workspaceId, String extensionKey, String key, String defaultValue);
    Map<String, String> getAll(String workspaceId, String extensionKey);
}

Example:

java
@Service
public class CrmApiClient {
    private final ExtensionConfigProvider config;

    public CrmApiClient(ExtensionConfigProvider config) {
        this.config = config;
    }

    public String getApiKey(String workspaceId) {
        return config.get(workspaceId, "crm-extension", "apiKey");
    }
}

Plugin Deployment

For IN_PROCESS extensions, place the built JAR in the metaone.plugins-path directory (default: plugins/):

bash
# Build the plugin
mvn -pl crm-extension package -DskipTests

# Copy to plugins directory
cp crm-extension/target/crm-extension-1.0.0.jar plugins/

The platform loads it automatically when the extension is installed and enabled. The JAR must be named {pluginId}-{version}.jar.

Plugin Lifecycle

JAR in plugins/ directory

    install()


   PF4J loads JAR
   MetaonePlugin.start()
   → createApplicationContext()   (host DataSource injected)
   → @Configuration classes refreshed
   → onPluginStart() called

    enable()  ──────────────────────── Capabilities/events registered

    disable()  ─────────────────────── Capabilities/events deregistered

    uninstall()


   onPluginStop() called
   Spring context closed
   PF4J unloads JAR

MetaOne Platform Documentation