-
Notifications
You must be signed in to change notification settings - Fork 1
Issue #13: [spring] Spring Boot integration — ItaraSpring fetch helper Implement ItaraSpring.get(Class) — a fetch helper that returns the wired proxy for a contract interface. The developer calls this in their @Configuration class to create their own bean explicitly. #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package io.itara.runtime; | ||
|
|
||
| /** | ||
| * Minimal Spring integration helper for Itara. | ||
| * | ||
| * <p>Purpose: | ||
| * Provides explicit access to Itara-managed component proxies so application | ||
| * developers can wire them into their own Spring configuration. | ||
| * | ||
| * <p>This is intentionally simple and explicit: | ||
| * <ul> | ||
| * <li>No Spring auto-configuration</li> | ||
| * <li>No automatic bean injection magic</li> | ||
| * <li>No dependency on Spring inside itara-common or itara-agent</li> | ||
| * </ul> | ||
| * | ||
| * <p>Typical usage: | ||
| * | ||
| * <pre>{@code | ||
| * @Configuration | ||
| * public class AppConfig { | ||
| * | ||
| * @Bean | ||
| * public UserClient userClient() { | ||
| * return ItaraSpring.get(UserClient.class); | ||
| * } | ||
| * } | ||
| * }</pre> | ||
| * | ||
| * <p>The returned instance is the Itara-wired proxy that was pre-registered | ||
| * by the agent during startup. | ||
| */ | ||
| public final class ItaraSpring { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know the GitHub Issue required this class, but I'm wondering if we should just skip it. It suggests that this is spring specific, which isn't true, and if we do introduce spring specific support later on, the name will be confusing. Could you remove it and simply call the registry directly instead? |
||
|
|
||
| private ItaraSpring() { | ||
| } | ||
|
|
||
| /** | ||
| * Retrieve a component by explicit component id and contract type. | ||
| * | ||
| * <p>Usually used internally or in advanced wiring scenarios where | ||
| * multiple implementations may exist. | ||
| * | ||
| * @param id component id from the topology configuration | ||
| * @param type expected contract/interface type | ||
| * @return the Itara-managed component instance or proxy | ||
| */ | ||
| public static <T> T get(String id, Class<T> type) { | ||
| ItaraRegistry registry = ItaraRegistry.instance(); | ||
| return registry.get(id, type); | ||
| } | ||
|
|
||
| /** | ||
| * Retrieve a component by contract/interface type. | ||
| * | ||
| * <p>Designed for Spring {@code @Bean} methods where the developer | ||
| * explicitly chooses which Itara component to expose as a Spring bean. | ||
| * | ||
| * <p>The registry must contain exactly one component matching the type. | ||
| * If multiple components match, an exception is thrown to avoid ambiguous | ||
| * wiring. | ||
| * | ||
| * @param type contract/interface type | ||
| * @return the pre-registered Itara proxy or activated component | ||
| */ | ||
| public static <T> T get(Class<T> type) { | ||
| ItaraRegistry registry = ItaraRegistry.instance(); | ||
| return registry.get(type); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
|
|
||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>io.itara.demo</groupId> | ||
| <artifactId>itara-demo</artifactId> | ||
| <version>1.0-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>gateway-springcomponent</artifactId> | ||
|
|
||
| <dependencies> | ||
|
|
||
| <dependency> | ||
| <groupId>io.itara.demo</groupId> | ||
| <artifactId>gateway-api</artifactId> | ||
| <scope>compile</scope> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>io.itara.demo</groupId> | ||
| <artifactId>calculator-api</artifactId> | ||
| <scope>compile</scope> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter</artifactId> | ||
| <version>4.0.6</version> | ||
| </dependency> | ||
|
|
||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-dependency-plugin</artifactId> | ||
| <version>3.6.1</version> | ||
| <executions> | ||
| <execution> | ||
| <id>copy-runtime-dependencies</id> | ||
| <phase>package</phase> | ||
| <goals> | ||
| <goal>copy-dependencies</goal> | ||
| </goals> | ||
| <configuration> | ||
| <outputDirectory> | ||
| ${project.build.directory}/lib | ||
| </outputDirectory> | ||
| <includeScope>runtime</includeScope> | ||
| </configuration> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package demo.gateway.springcomponent; | ||
|
|
||
| import demo.gateway.api.GatewayService; | ||
| import io.itara.runtime.ItaraSpring; | ||
| import org.springframework.boot.CommandLineRunner; | ||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| import org.springframework.context.annotation.Bean; | ||
|
|
||
| import java.util.logging.Logger; | ||
|
|
||
| /** | ||
| * Demo entry point. | ||
| * | ||
| * The agent has already wired everything before this runs. | ||
| * All we do is obtain gateway from ItaraSpring and make a few calls. | ||
| * | ||
| * This same main works for both topologies: | ||
| * - Direct: calculator runs in this JVM, called as a method | ||
| * - HTTP: calculator runs in a separate JVM, called over the network | ||
| * | ||
| * The code here does not change. The wiring config changes. | ||
| * | ||
| * Run with: | ||
| * java -Ditara.lib.dir=itara-libs \ | ||
| * -Ditara.config=itara-demo/wiring-direct-spring.yaml \ | ||
| * -Ditara.nodes=calculatorNode,gatewayNodeSpring \ | ||
| * -javaagent:itara-agent/target/itara-agent-1.0-SNAPSHOT.jar \ | ||
| * -cp "itara-demo/calculator-component/target/calculator-component-1.0-SNAPSHOT.jar;itara-demo/gateway-springcomponent/target/lib/*;itara-demo/gateway-springcomponent/target/gateway-springcomponent-1.0-SNAPSHOT.jar" \ | ||
| * demo.gateway.springcomponent.DemoMain | ||
| */ | ||
| @SpringBootApplication | ||
| public class DemoMain { | ||
|
|
||
| private static final Logger log = Logger.getLogger(DemoMain.class.getName()); | ||
|
|
||
|
|
||
| public static void main(String[] args) throws Exception { | ||
| SpringApplication.run(DemoMain.class, args); | ||
| } | ||
|
|
||
| @Bean | ||
| public CommandLineRunner runner(final GatewayService gateway) { | ||
| return args -> { | ||
| log.info("=".repeat(50)); | ||
| log.info("Itara Demo starting..."); | ||
| log.info("=".repeat(50)); | ||
|
|
||
| // Small pause to let the agent's HTTP server start if needed | ||
| Thread.sleep(500); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot to delete this in the other demo code, but it shouldn't be needed anymore, I think you can remove this sleep. When it's manually started, the user handles it, when docker-compose runs it, there is a dependency. It shouldn't be necessary. |
||
|
|
||
| log.info(""); | ||
| log.info("--- Making calls ---"); | ||
| log.info(""); | ||
|
|
||
| log.info(gateway.calculate(3, 4)); | ||
| log.info(""); | ||
| log.info(gateway.calculate(10, 25)); | ||
| log.info(""); | ||
| log.info(gateway.calculate(100, 200)); | ||
|
|
||
| log.info(""); | ||
| log.info("=".repeat(50)); | ||
| log.info("Done."); | ||
| log.info("=".repeat(50)); | ||
| }; | ||
| } | ||
|
|
||
| @Bean | ||
| public GatewayService gateway() { | ||
| log.info("Making bean from: " + GatewayService.class.getName()); | ||
| return ItaraSpring.get(GatewayService.class); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package demo.gateway.springcomponent; | ||
|
|
||
| import demo.calculator.api.CalculatorService; | ||
| import demo.gateway.api.GatewayService; | ||
| import io.itara.api.ItaraActivator; | ||
| import io.itara.runtime.ItaraRegistry; | ||
|
|
||
| import java.util.logging.Logger; | ||
|
|
||
| /** | ||
| * Activator for the gateway component. | ||
| * | ||
| * Pulls CalculatorService from the registry — the registry returns either | ||
| * a direct instance (collocated topology) or an HTTP proxy (remote topology). | ||
| * This code does not change between the two topologies. That is the point. | ||
| */ | ||
| public class GatewayActivator implements ItaraActivator<GatewayService> { | ||
|
|
||
| private static final Logger log = Logger.getLogger(GatewayActivator.class.getName()); | ||
|
|
||
| @Override | ||
| public GatewayService activate(ItaraRegistry registry) { | ||
| log.info("[GatewayActivator] Pulling calculator from registry..."); | ||
| CalculatorService calculator = registry.get("calculator", CalculatorService.class); | ||
| log.info("[GatewayActivator] Creating GatewayServiceImpl"); | ||
| return new GatewayServiceImpl(calculator); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package demo.gateway.springcomponent; | ||
|
|
||
| import demo.calculator.api.CalculatorService; | ||
| import demo.gateway.api.GatewayService; | ||
|
|
||
| import java.util.logging.Logger; | ||
|
|
||
| /** | ||
| * The gateway implementation. | ||
| * Accepts a request, delegates to CalculatorService, prints and returns the result. | ||
| * | ||
| * Has no knowledge of whether CalculatorService is a direct call or HTTP. | ||
| * The registry provides whichever the topology config dictates. | ||
| */ | ||
| public class GatewayServiceImpl implements GatewayService { | ||
|
|
||
| private static final Logger log = Logger.getLogger(GatewayServiceImpl.class.getName()); | ||
|
|
||
| private final CalculatorService calculator; | ||
|
|
||
| public GatewayServiceImpl(CalculatorService calculator) { | ||
| this.calculator = calculator; | ||
| } | ||
|
|
||
| @Override | ||
| public String calculate(int a, int b) { | ||
| log.info("[Gateway] Received request: add(" + a + ", " + b + ")"); | ||
| int result = calculator.add(a, b); | ||
| String message = "The result of " + a + " + " + b + " = " + result; | ||
| log.info("[Gateway] Returning: " + message); | ||
| return message; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| component-id=gateway | ||
| activator=demo.gateway.springcomponent.GatewayActivator |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # wiring-direct.yaml | ||
| # Both components run in the same JVM. | ||
| # Gateway calls Calculator as a direct method call — no network involved. | ||
|
|
||
| nodes: | ||
| - id: "gatewayNodeSpring" | ||
| component: "gateway" | ||
|
|
||
| - id: "calculatorNode" | ||
| component: "calculator" | ||
|
|
||
| connections: | ||
| - from: "gatewayNodeSpring" | ||
| to: "calculatorNode" | ||
| type: "direct" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| nodes: | ||
| - id: "gatewayNodeSpring" | ||
| component: "gateway" | ||
|
|
||
| - id: "calculatorNode" | ||
| component: "calculator" | ||
|
|
||
| connections: | ||
| - from: "gatewayNodeSpring" | ||
| to: "calculatorNode" | ||
| type: http | ||
| host: "localhost" | ||
| port: 8081 | ||
| serializer: "json" |
Uh oh!
There was an error while loading. Please reload this page.