Skip to content

Move reusable code from the Camel Integration Capability#68

Merged
orpiske merged 1 commit intowanaku-ai:mainfrom
orpiske:add-runtimes
Feb 9, 2026
Merged

Move reusable code from the Camel Integration Capability#68
orpiske merged 1 commit intowanaku-ai:mainfrom
orpiske:add-runtimes

Conversation

@orpiske
Copy link
Copy Markdown
Contributor

@orpiske orpiske commented Feb 9, 2026

So that we can create a Camel Context plugin that can be loaded in build time and in runtime.

Ref: wanaku-ai/camel-integration-capability#56

Summary by Sourcery

Introduce a new Camel-specific runtime structure and plugin for the capabilities SDK, extracting shared runtime logic into reusable modules and wiring them into the build.

New Features:

  • Add a Camel runtime parent module with common utilities, MCP spec handling, and gRPC tool/resource endpoints for integrating Camel routes as Wanaku tools and resources.
  • Introduce a Camel ContextServicePlugin that boots a gRPC server, handles configuration, downloads required resources, and registers with the Wanaku discovery and services APIs.

Enhancements:

  • Restructure the runtime into a new capabilities-runtimes aggregator with a shared runtimes-common module consumed by Camel runtimes.
  • Update BOM, parent POMs, and Java tool archetype to depend on the new runtimes-common and Camel runtime artifacts, and centralize Camel/JGit/Jackson YAML versions in parent configuration.

Tests:

  • Add unit and integration tests plus sample MCP spec and configuration/resources to validate MCP rules loading, Camel plugin discovery, and end‑to‑end plugin behavior.

So that we can create a Camel Context plugin that can be loaded in build time and in runtime.

Ref: wanaku-ai/camel-integration-capability#56
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Feb 9, 2026

Reviewer's Guide

Introduces a new modular runtimes layout and a Camel-specific runtime plugin: the generic runtime is split into a capabilities-runtimes parent with a common module and a Camel runtime aggregator, and a new Camel plugin module is added that can be discovered via Camel’s ContextServicePlugin SPI, load configuration, download routes/rules/dependencies, start a gRPC server for tools/resources, and register itself with Wanaku discovery/services.

Sequence diagram for CamelIntegrationPlugin load lifecycle

sequenceDiagram
    actor CamelApp
    participant CamelContext
    participant CamelIntegrationPlugin
    participant PluginConfiguration
    participant InitializerFactory
    participant Initializer
    participant ServicesHttpClient
    participant DownloaderFactory
    participant ResourceListBuilder
    participant ResourceDownloaderCallback
    participant RegistrationManager
    participant DiscoveryService
    participant McpRulesManager
    participant GrpcServer

    CamelApp->>CamelContext: start()
    CamelContext->>CamelIntegrationPlugin: load(camelContext)
    activate CamelIntegrationPlugin

    CamelIntegrationPlugin->>PluginConfiguration: load()
    PluginConfiguration-->>CamelIntegrationPlugin: PluginConfiguration
    CamelIntegrationPlugin->>PluginConfiguration: validate()

    CamelIntegrationPlugin->>CamelIntegrationPlugin: create dataDir

    CamelIntegrationPlugin->>InitializerFactory: createInitializer(initFrom, dataDir)
    InitializerFactory-->>Initializer: Initializer
    CamelIntegrationPlugin->>Initializer: initialize()

    CamelIntegrationPlugin->>ServicesHttpClient: new(serviceConfig)
    ServicesHttpClient-->>CamelIntegrationPlugin: httpClient

    CamelIntegrationPlugin->>DownloaderFactory: new(httpClient, dataDir)

    CamelIntegrationPlugin->>ResourceListBuilder: newBuilder()
    ResourceListBuilder-->>CamelIntegrationPlugin: builder
    CamelIntegrationPlugin->>ResourceListBuilder: addRoutesRef(routesRef)
    CamelIntegrationPlugin->>ResourceListBuilder: addRulesRef(rulesRef)
    CamelIntegrationPlugin->>ResourceListBuilder: addDependenciesRef(dependenciesRef)
    ResourceListBuilder-->>CamelIntegrationPlugin: buildForPlugin(): resources

    CamelIntegrationPlugin->>ResourceDownloaderCallback: new(downloaderFactory, resources)

    CamelIntegrationPlugin->>CamelIntegrationPlugin: newServiceTarget()
    CamelIntegrationPlugin->>CamelIntegrationPlugin: newRegistrationManager(target, callback, serviceConfig)
    CamelIntegrationPlugin->>RegistrationManager: start()

    RegistrationManager->>DiscoveryService: register(serviceTarget)
    DiscoveryService-->>RegistrationManager: registration ok
    RegistrationManager->>ResourceDownloaderCallback: onRegistration(...)
    activate ResourceDownloaderCallback
    ResourceDownloaderCallback->>ResourceDownloaderCallback: downloadResources()
    ResourceDownloaderCallback-->>ResourceDownloaderCallback: downloadedResources
    deactivate ResourceDownloaderCallback

    CamelIntegrationPlugin->>ResourceDownloaderCallback: waitForDownloads()
    alt downloads ok
        CamelIntegrationPlugin->>McpRulesManager: new(serviceName, rulesPath)
        CamelIntegrationPlugin->>McpRulesManager: loadMcpSpecAndRegister(toolTransformer, resourceTransformer)
        McpRulesManager-->>CamelIntegrationPlugin: McpSpec

        CamelIntegrationPlugin->>GrpcServer: new().addService(CamelTool, CamelResource, ProvisionBase)
        GrpcServer-->>CamelIntegrationPlugin: server
        CamelIntegrationPlugin->>GrpcServer: start()
    else failure
        CamelIntegrationPlugin-->>CamelContext: throw IllegalStateException
    end

    deactivate CamelIntegrationPlugin
Loading

Class diagram for CamelIntegrationPlugin, configuration, and initialization

classDiagram
    class CamelIntegrationPlugin {
        -Server grpcServer
        -RegistrationManager registrationManager
        -PluginConfiguration config
        +load(CamelContext camelContext) void
        +unload(CamelContext camelContext) void
        -newServiceTarget() ServiceTarget
        -newRegistrationManager(ServiceTarget serviceTarget, ResourceDownloaderCallback resourcesDownloaderCallback, ServiceConfig serviceConfig) RegistrationManager
        -createMcpSpec(ServicesHttpClient servicesClient, Map downloadedResources) McpSpec
    }

    class PluginConfiguration {
        -String registrationUrl
        -int grpcPort
        -String registrationAnnounceAddress
        -String serviceName
        -String routesRef
        -String rulesRef
        -String tokenEndpoint
        -String clientId
        -String clientSecret
        -String dependenciesRef
        -String repositoriesList
        -String dataDir
        -String initFrom
        -int retries
        -int retryWaitSeconds
        -long initialDelay
        -long period
        -boolean noWait
        +static load() PluginConfiguration
        -static getConfigValue(Properties props, String propertyKey, String envKey, String defaultValue) String
        +validate() void
        +getRegistrationUrl() String
        +getGrpcPort() int
        +getRegistrationAnnounceAddress() String
        +getServiceName() String
        +getRoutesRef() String
        +getRulesRef() String
        +getTokenEndpoint() String
        +getClientId() String
        +getClientSecret() String
        +getDependenciesRef() String
        +getRepositoriesList() String
        +getDataDir() String
        +getInitFrom() String
        +getRetries() int
        +getRetryWaitSeconds() int
        +getInitialDelay() long
        +getPeriod() long
        +isNoWait() boolean
    }

    class InitializerFactory {
        +createInitializer(String initFrom, Path dataDir) Initializer
    }

    class Initializer {
        <<interface>>
        +initialize() void
    }

    class GitInitializer {
        -String gitRepoUrl
        -Path dataDir
        -Path clonedRepoPath
        +GitInitializer(String gitRepoUrl, Path dataDir)
        +initialize() void
        +getClonedRepoPath() Path
    }

    class NoOpInitializer {
        +NoOpInitializer()
        +initialize() void
    }

    class McpRulesManager {
        -String name
        -String routesRules
        +McpRulesManager(String name, String routesRules)
        -loadMcpSpec() McpSpec
        -registerDefinitions(Map definitions, RulesTransformer transformer) void
        +loadMcpSpecAndRegister(RulesTransformer toolTransformer, RulesTransformer resourceTransformer) McpSpec
    }

    class McpRulesReader {
        +readMcpSpecFromFile(String filePath) McpSpec
        +readMcpSpecFromFile(File file) McpSpec
        +readMcpSpecFromInputStream(InputStream inputStream) McpSpec
        +readMcpSpecFromString(String yamlContent) McpSpec
        +readFromFile(String filePath) Tool
        +readFromFile(File file) Tool
        +readFromInputStream(InputStream inputStream) Tool
        +readFromString(String yamlContent) Tool
    }

    class ProvisionBase {
        +ProvisionBase(String name)
        +provision(ProvisionRequest request, StreamObserver responseObserver) void
        +properties() Map
    }

    CamelIntegrationPlugin --> PluginConfiguration : uses
    CamelIntegrationPlugin --> InitializerFactory : uses
    InitializerFactory --> Initializer : creates
    GitInitializer ..|> Initializer
    NoOpInitializer ..|> Initializer
    CamelIntegrationPlugin --> McpRulesManager : uses
    McpRulesManager --> McpRulesReader : uses
    CamelIntegrationPlugin ..> ProvisionBase : creates
Loading

Class diagram for downloaders, MCP model, gRPC services, and utilities

classDiagram
    class Downloader {
        <<interface>>
        +downloadResource(ResourceRefs resourceName, Map downloadedResources) void
    }

    class DownloaderFactory {
        -ServicesHttpClient servicesHttpClient
        -Path dataDir
        -FileDownloader fileDownloader
        -DataStoreDownloader dataStoreDownloader
        +DownloaderFactory(ServicesHttpClient servicesHttpClient, Path dataDir)
        +getDownloader(URI uri) Downloader
        -getDataStoreDownloader() DataStoreDownloader
        -getFileDownloader() FileDownloader
    }

    class FileDownloader {
        -Path dataDir
        +FileDownloader(Path dataDir)
        +downloadResource(ResourceRefs resourceName, Map downloadedResources) void
    }

    class DataStoreDownloader {
        -ServicesHttpClient servicesHttpClient
        -Path dataDir
        +DataStoreDownloader(ServicesHttpClient servicesHttpClient, Path dataDir)
        +downloadResource(ResourceRefs resourceName, Map downloadedResources) void
    }

    class ResourceRefs {
        <<record>>
        +resourceType() ResourceType
        +ref() URI
        +newRoutesRef(String routesRef) ResourceRefs
        +newRulesRef(String rulesRef) ResourceRefs
        +newDependencyRef(String dependencyRef) ResourceRefs
    }

    class ResourceType {
        <<enum>>
        ROUTES_REF
        RULES_REF
        DEPENDENCY_REF
    }

    class ResourceDownloaderCallback {
        -List resources
        -CountDownLatch countDownLatch
        -DownloaderFactory downloaderFactory
        -Map downloadedResources
        +ResourceDownloaderCallback(DownloaderFactory downloaderFactory, List resources)
        +onPing(RegistrationManager manager, ServiceTarget target, int status) void
        +onRegistration(RegistrationManager manager, ServiceTarget target) void
        +onDeregistration(RegistrationManager manager, ServiceTarget target, int status) void
        -downloadResources() void
        +waitForDownloads() boolean
        +getDownloadedResources() Map
    }

    class ResourceListBuilder {
        -List resources
        -boolean hasRoutesRef
        +newBuilder() ResourceListBuilder
        +addRoutesRef(String routesRef) ResourceListBuilder
        +addRulesRef(String rulesRef) ResourceListBuilder
        +addDependenciesRef(String dependenciesRef) ResourceListBuilder
        +build() List
        +buildForPlugin() List
    }

    class McpSpec {
        -McpContent mcp
        +McpSpec()
        +getMcp() McpContent
        +setMcp(McpContent mcp) void
    }

    class McpContent {
        -McpEntityWrapper tools
        -McpEntityWrapper resources
        +McpContent()
        +getTools() McpEntityWrapper
        +setTools(McpEntityWrapper tools) void
        +getResources() McpEntityWrapper
        +setResources(McpEntityWrapper resources) void
    }

    class McpEntityWrapper {
        -Map tools
        +McpEntityWrapper()
        +getDefinitions() Map
        +setTools(Map tools) void
    }

    class McpEntityDeserializer

    class Definition {
        -Route route
        -String description
        -List properties
        -String namespace
        +Definition()
        +getRoute() Route
        +setRoute(Route route) void
        +getDescription() String
        +setDescription(String description) void
        +getProperties() List
        +setProperties(List properties) void
        +getNamespace() String
        +setNamespace(String namespace) void
    }

    class Route {
        -String uri
        -String id
        +Route()
        +getUri() String
        +setUri(String uri) void
        +getId() String
        +setId(String id) void
    }

    class Property {
        -String name
        -String type
        -String description
        -boolean required
        -Mapping mapping
        +Property()
        +getName() String
        +setName(String name) void
        +getType() String
        +setType(String type) void
        +getDescription() String
        +setDescription(String description) void
        +isRequired() boolean
        +setRequired(boolean required) void
        +getMapping() Mapping
        +setMapping(Mapping mapping) void
    }

    class Mapping {
        -String type
        -String name
        +Mapping()
        +getType() String
        +setType(String type) void
        +getName() String
        +setName(String name) void
    }

    class Tool {
        -McpEntityWrapper toolsWrapper
        +Tool()
        +getTools() Map
        +setToolsWrapper(McpEntityWrapper toolsWrapper) void
    }

    class CamelTool {
        -McpSpec mcpSpec
        -CamelContext camelContext
        +CamelTool(CamelContext camelContext, McpSpec spec)
        +getTools(McpSpec mcpSpec) Map
        +invokeTool(ToolInvokeRequest request, StreamObserver responseObserver) void
        -reportRouteFailure(StreamObserver responseObserver, Exception e, Definition toolDefinition) void
        -resolveEndpoint(Definition toolDefinition, CamelContext camelContext) String
        -extractHeaderParameters(ToolInvokeRequest request, Definition toolDefinition) Map
    }

    class CamelResource {
        -McpSpec mcpSpec
        -CamelContext camelContext
        +CamelResource(CamelContext camelContext, McpSpec mcpSpec)
        +getResources(McpSpec mcpSpec) Map
        +resourceAcquire(ResourceRequest request, StreamObserver responseObserver) void
        -resolveEndpoint(Definition definition, CamelContext camelContext) String
        -reportRouteFailure(StreamObserver responseObserver, Exception e, Definition definition) void
    }

    class WanakuRoutesLoader {
        -String dependenciesList
        -String repositoriesList
        -DependencyDownloaderClassLoader cl
        -MavenDependencyDownloader downloader
        +WanakuRoutesLoader(String dependenciesList, String repositoriesList)
        +loadRoute(CamelContext context, String path) void
        -downloadDependencies(CamelContext camelContext) void
        -createDownloader(DependencyDownloaderClassLoader cl) MavenDependencyDownloader
        -createClassLoader() DependencyDownloaderClassLoader
    }

    class FileUtil {
        +untilAvailable(File input, boolean isDirectory) boolean
    }

    class GavUtil {
        +group(String gav) String
        +artifact(String gav) String
        +version(String gav) String
    }

    DownloaderFactory --> Downloader : returns
    FileDownloader ..|> Downloader
    DataStoreDownloader ..|> Downloader

    ResourceRefs --> ResourceType
    ResourceDownloaderCallback --> DownloaderFactory
    ResourceDownloaderCallback --> ResourceRefs

    McpSpec o-- McpContent
    McpContent o-- McpEntityWrapper
    McpEntityWrapper o-- Definition
    Definition o-- Route
    Definition o-- Property
    Property o-- Mapping

    CamelTool --> McpSpec
    CamelTool --> Definition
    CamelResource --> McpSpec
    CamelResource --> Definition

    WanakuRoutesLoader --> GavUtil
    FileDownloader --> FileUtil
Loading

File-Level Changes

Change Details Files
Refactor runtime packaging into a new capabilities-runtimes parent with common and Camel-specific modules and update BOM/archetype references.
  • Rename and move the previous capabilities-runtime module into capabilities-runtimes/capabilities-runtimes-common with an updated parent pom and artifactId.
  • Introduce a new capabilities-runtimes aggregator pom that hosts runtimes-common and a Camel runtime aggregator.
  • Add a capabilities-runtimes-camel pom that defines Camel-specific properties and modules, and update the top-level pom.xml to reference capabilities-runtimes instead of capabilities-runtime.
  • Adjust the BOM and Java tool archetype to depend on capabilities-runtimes-common, and add BOM entries for the Camel common and plugin artifacts.
  • Add shared version properties for Camel, JGit, and jackson-dataformat-yaml in capabilities-parent.
capabilities-runtime/pom.xml
capabilities-runtimes/pom.xml
capabilities-runtimes/capabilities-runtimes-camel/pom.xml
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/pom.xml
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/pom.xml
capabilities-bom/pom.xml
capabilities-archetypes/capabilities-archetypes-java-tool/src/main/resources/archetype-resources/pom.xml
capabilities-parent/pom.xml
pom.xml
Add a reusable Camel runtime common library encapsulating MCP spec parsing, resource/tool registration, resource downloading, and route loading utilities.
  • Introduce model classes for MCP specs, tools, resources, routes, mappings, and properties, plus a YAML-based McpRulesReader and McpRulesManager for loading and registering MCP definitions.
  • Add gRPC service implementations CamelTool and CamelResource that invoke or consume Camel routes based on MCP definitions and map arguments to Camel headers via a pluggable HeaderMapper strategy.
  • Create WanakuRoutesLoader and GavUtil helpers to download route dependencies via Camel’s MavenDependencyDownloader and load routes from external resources.
  • Implement downloader infrastructure (Downloader, DownloaderFactory, ResourceRefs/ResourceType, FileDownloader, DataStoreDownloader, ResourceDownloaderCallback, ResourceListBuilder) to fetch routes/rules/dependencies from datastore:// or file:// URIs and expose them as local files.
  • Add initialization helpers (Initializer, InitializerFactory, GitInitializer, NoOpInitializer) plus FileUtil for file availability watching, and a ProvisionBase gRPC service base for configuration provisioning.
  • Add WanakuToolTransformer/WanakuToolRuleProcessor and WanakuResourceTransformer/WanakuResourceRuleProcessor to convert MCP definitions into Wanaku ToolReference/ResourceReference and register/deregister them with the services client.
  • Add tests and test resources for MCP spec parsing (McpRulesReaderTest and sample-tool-spec.yaml).
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/grpc/CamelTool.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/grpc/CamelResource.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/grpc/ProvisionBase.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/WanakuRoutesLoader.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/McpRulesManager.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/McpRulesReader.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/FileUtil.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/GavUtil.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/downloader/*.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/model/*.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/spec/rules/**/*.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/test/java/ai/wanaku/capabilities/sdk/runtime/camel/util/McpRulesReaderTest.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/test/resources/sample-tool-spec.yaml
Introduce a Camel Integration ContextServicePlugin that wires configuration, discovery, resource download, MCP loading, and gRPC endpoints into an existing Camel application.
  • Add CamelIntegrationPlugin implementing ContextServicePlugin that on load reads PluginConfiguration, creates data directory, runs Initializer, builds ServiceConfig/ServicesHttpClient, sets up DownloaderFactory and ResourceDownloaderCallback, registers with discovery via ZeroDepRegistrationManager, waits for resources, builds an McpSpec using McpRulesManager, and starts a gRPC server exposing CamelTool, CamelResource, and ProvisionBase services.
  • Implement PluginConfiguration with properties-file + environment-variable override semantics, validation of required settings (registration URL, client ID/secret), and getters for all plugin configuration knobs.
  • Provide default plugin properties in camel-integration-capability.properties documenting all configurable keys and their env var equivalents.
  • Add SPI metadata (META-INF/services/org.apache.camel.spi.ContextServicePlugin) so Camel can auto-discover the plugin.
  • Add plugin tests: CamelIntegrationPluginTest for SPI discovery and basic behavior; CamelIntegrationPluginIT for manual integration testing against a real Wanaku instance, with supporting test resources (cat-facts-rules.yaml, log4j2.properties).
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/plugin/CamelIntegrationPlugin.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/plugin/PluginConfiguration.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/main/resources/camel-integration-capability.properties
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/main/resources/META-INF/services/org.apache.camel.spi.ContextServicePlugin
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/test/java/ai/wanaku/capabilities/sdk/runtime/camel/plugin/CamelIntegrationPluginTest.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/test/java/ai/wanaku/capabilities/sdk/runtime/camel/plugin/CamelIntegrationPluginIT.java
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/test/resources/cat-facts-rules.yaml
capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-plugin/src/test/resources/log4j2.properties

Possibly linked issues

  • #Camel Code Execution Engine: PR adds Camel runtime/common modules and plugin that implement SDK integration for the Camel Code Execution Engine.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In WanakuToolTransformer, the required field is an instance-level list that accumulates required properties across multiple tools; make this a local list inside transform (or parseProperties) so each tool gets an isolated required list.
  • Both CamelTool.resolveEndpoint and CamelResource.resolveEndpoint blindly look up the route by toolDefinition.getRoute().getId(); consider handling the common case where only a URI is defined (e.g. fall back to using the uri directly or validating that id is present) to avoid null route lookups or NPEs with the current YAML examples.
  • In CamelIntegrationPluginTest.testLoadFailsWithoutConfiguration, plugin.load(null) will eventually dereference the CamelContext; either construct a real CamelContext (e.g. DefaultCamelContext) or mock it so the test is exercising configuration failure rather than causing a NullPointerException.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `WanakuToolTransformer`, the `required` field is an instance-level list that accumulates required properties across multiple tools; make this a local list inside `transform` (or `parseProperties`) so each tool gets an isolated `required` list.
- Both `CamelTool.resolveEndpoint` and `CamelResource.resolveEndpoint` blindly look up the route by `toolDefinition.getRoute().getId()`; consider handling the common case where only a URI is defined (e.g. fall back to using the `uri` directly or validating that `id` is present) to avoid `null` route lookups or NPEs with the current YAML examples.
- In `CamelIntegrationPluginTest.testLoadFailsWithoutConfiguration`, `plugin.load(null)` will eventually dereference the `CamelContext`; either construct a real `CamelContext` (e.g. `DefaultCamelContext`) or mock it so the test is exercising configuration failure rather than causing a `NullPointerException`.

## Individual Comments

### Comment 1
<location> `capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/downloader/ResourceDownloaderCallback.java:42-43` </location>
<code_context>
+    public void onDeregistration(RegistrationManager manager, ServiceTarget target, int status) {}
+
+    private void downloadResources() {
+        if (resources == null || resources.isEmpty()) {
+            LOG.debug("No resources to download");
+            return;
+        }
</code_context>

<issue_to_address>
**issue (bug_risk):** Empty resources list causes waitForDownloads() to block indefinitely.

When `resources` is null or empty, this method returns without decrementing the latch, so `waitForDownloads()` will block indefinitely and plugin initialization will never finish. Ensure the latch is decremented even when there are no resources, and decide at the call site whether that case should be treated as success or failure.
</issue_to_address>

### Comment 2
<location> `capabilities-runtimes/capabilities-runtimes-camel/capabilities-runtimes-camel-common/src/main/java/ai/wanaku/capabilities/sdk/runtime/camel/util/FileUtil.java:21-30` </location>
<code_context>
+        WatchService watchService = FileSystems.getDefault().newWatchService();
</code_context>

<issue_to_address>
**issue (bug_risk):** WatchService is never closed, which can leak system resources.

The `WatchService` created here is never closed, so repeated calls to `untilAvailable` can leak OS resources. Please wrap its usage in try/finally or try-with-resources and invoke `watchService.close()` when finished, including on exceptions.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +42 to +43
if (resources == null || resources.isEmpty()) {
LOG.debug("No resources to download");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Empty resources list causes waitForDownloads() to block indefinitely.

When resources is null or empty, this method returns without decrementing the latch, so waitForDownloads() will block indefinitely and plugin initialization will never finish. Ensure the latch is decremented even when there are no resources, and decide at the call site whether that case should be treated as success or failure.

Comment on lines +21 to +30
WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = isDirectory ? input.toPath() : input.getParentFile().toPath();

if (input.exists()) {
LOG.info("File {} already available", input);
return true;
}

// We watch for both the file creation and truncation
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): WatchService is never closed, which can leak system resources.

The WatchService created here is never closed, so repeated calls to untilAvailable can leak OS resources. Please wrap its usage in try/finally or try-with-resources and invoke watchService.close() when finished, including on exceptions.

@orpiske orpiske merged commit 5ad6e17 into wanaku-ai:main Feb 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant