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