> ## Documentation Index
> Fetch the complete documentation index at: https://platform.docs.zenoo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin Connector Development

> Developing OSGI plugin connectors for modular Hub extensions

# Plugin Connector Development

> **Related Documentation**
>
> * [Connectors Overview](/technical-specification/hub-backend/connectors/overview) - Complete guide to Hub's connector system
> * [HTTP Connector](/technical-specification/hub-backend/connectors/httpconnector) - Built-in HTTP connector features
> * [Custom Connector Development](/technical-specification/hub-backend/connectors/custom-development) - Embedded connector development
> * [DSL Reference](/technical-specification/hub-backend/engine/dsl) - Exchange and connector DSL syntax

This guide covers developing OSGI plugin connectors, which are modular, hot-deployable connector extensions for Hub.

## Table of Contents

* [Plugin vs Embedded Connectors](#plugin-vs-embedded-connectors)
* [Development Setup](#development-setup)
* [Plugin Implementation](#plugin-implementation)
  * [Basic Plugin Connector](#basic-plugin-connector)
  * [Plugin DSL Component](#plugin-dsl-component)

***

## Plugin vs Embedded Connectors

### Comparison Table

| Feature                | Embedded Connectors        | Plugin Connectors         |
| ---------------------- | -------------------------- | ------------------------- |
| **Deployment**         | Built into application JAR | Separate OSGI bundles     |
| **Hot Deploy**         | Requires restart           | Runtime install/uninstall |
| **Interface**          | `CustomConnector`          | `plugin.sdk.Connector`    |
| **Validation**         | Rich `PayloadSpec`         | Manual input processing   |
| **Error Handling**     | Full exception hierarchy   | Basic error handling      |
| **Spring Integration** | Full framework support     | Limited Spring features   |
| **Testing**            | `MockConnector` support    | Manual mocking            |
| **Configuration**      | Spring configuration       | OSGI service properties   |
| **Packaging**          | Part of main application   | Independent JAR/bundle    |

### When to Use Plugin Connectors

**Use Plugin Connectors for:**

* Third-party integrations
* Customer-specific connectors
* Experimental connectors
* Connectors with different release cycles
* Connectors requiring hot deployment
* Modular architecture requirements

**Use Embedded Connectors for:**

* Core application functionality
* Stable, well-tested connectors
* Connectors requiring complex Spring integration
* Performance-critical connectors
* Connectors with rich validation needs

***

## Development Setup

### Project Structure

```
my-plugin/
├── src/main/java/
│   └── com/company/plugin/
│       ├── MyConnector.groovy
│       └── DSLComponent.groovy
└── build.gradle
```

### Dependencies

build.gradle

```gradle theme={null}
plugins {
    id 'groovy'
    id 'biz.aQute.bnd.builder' version '7.1.0'
}

dependencies {
    // Hub Plugin SDK
    implementation 'com.zenoo.hub:hub-plugin-sdk:1.0.0'

    // Testing
    testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0'
    testImplementation 'org.mockito:mockito-core:4.8.0'
}

// OSGI Bundle Configuration
jar {
    manifest {
        attributes(
                'Bundle-SymbolicName': 'my-plugin',
                'Bundle-Version': '0.1.0',
                'Service-Component': '*'
        )
    }
}
```

***

## Plugin Implementation

### Basic Plugin Connector

```groovy theme={null}
package com.company.plugin

import com.zenoo.hub.dsl.attribute.Attribute
import com.zenoo.hub.dsl.attribute.AttributeBuilder
import com.zenoo.hub.plugin.sdk.Connector
import groovy.util.logging.Slf4j
import org.osgi.service.component.annotations.Component
import reactor.core.publisher.Mono

@Slf4j
@Component(immediate = true)
class MyConnector implements Connector {

    @Override
    String name() {
        return 'my-plugin'
    }

    @Override
    Mono<Attribute> exchange(Attribute input) {
        log.info("Processing input: {}", input)
         // Return result
         return Mono.just(AttributeBuilder.of([
             processed: input,
             timestamp: System.currentTimeMillis(),
             success: true,
             plugin: name()
         ]))
    }

}
```

### Plugin DSL Component

Plugins can also define DSL components that provide reusable workflows, functions, and exposed endpoints. This is done by implementing the `ComponentFactory` interface:

```groovy theme={null}
package com.company.plugin

import com.zenoo.hub.plugin.sdk.ComponentBuilder
import com.zenoo.hub.plugin.sdk.ComponentFactory
import org.osgi.service.component.annotations.Component

@Component
class MyPluginDSLComponent implements ComponentFactory {

    @Override
    ComponentBuilder define() {
        return ComponentBuilder.component {
            dependencies {
                connector 'my-plugin'
            }

            // Exposed function accessible via Client API
            exposed('process-data') {
                payload ->
                    function('validate-input') {
                        input payload
                        namespace validated
                    }

                    exchange('process') {
                        connector 'my-plugin'
                        config validated
                    }.orError {
                       error([error: 'Processing failed', timestamp: System.currentTimeMillis()])
                    }
            }

            // Reusable workflow
            workflow('document-processing') {
                route('upload') {
                    uri '/plugin/upload'
                    validate {
                        required 'document'
                        optional 'options'
                    }
                }

                function('process-document') {
                    input payload
                    namespace result
                }

                route('results') {
                    uri '/plugin/results'
                    export data: result
                    terminal()
                }
            }

            // Reusable function
            function('validate-input') {
                input ->
                    mapper('input-mapper') {
                        payload -> [
                            data: payload.data,
                            isValid: payload.data != null,
                            timestamp: System.currentTimeMillis()
                        ]
                    }
            }

            // Data mapper
            mapper('input-mapper') {
                payload -> [
                    data: payload.data,
                    isValid: payload.data != null,
                    timestamp: System.currentTimeMillis()
                ]
            }
        }
    }
}
```
