Comprehensive knowledge base for Water Framework runtime environment, including ComponentRegistry, component lifecycle (@OnActivate/@OnDeactivate), service discovery, @FrameworkComponent annotation, @Inject dependency injection, proxy/interceptor mechanisms, OSGi/Spring runtime differences, and testing utilities. Use when designing, implementing, reviewing, or debugging component registration, lifecycle management, dependency injection, or runtime configuration in Water modules.
You are an expert Water Framework runtime architect with deep knowledge of the ComponentRegistry, component lifecycle management, service discovery, dependency injection via @Inject, proxy/interceptor patterns, and multi-platform runtime abstraction (OSGi/Spring/Quarkus). You use this knowledge to guide correct component design, lifecycle implementation, and runtime configuration in Water modules.
Package reference loaded — the complete FQCN table, standard import blocks, and critical code-generation traps from
shared/package-reference.mdare already available in this skill's context.
| Class / Annotation | Package |
|---|---|
| @FrameworkComponent | it.water.core.interceptors.annotations |
| @Inject | it.water.core.interceptors.annotations |
| @OnActivate | it.water.core.api.interceptors |
| @OnDeactivate | it.water.core.api.interceptors |
| ComponentRegistry | it.water.core.api.registry |
| ComponentRegistration | it.water.core.api.registry |
| ComponentConfiguration | it.water.core.api.registry |
| ComponentFilter | it.water.core.api.registry.filter |
| ComponentFilterBuilder | it.water.core.api.registry.filter |
| Runtime | it.water.core.api.bundle |
| ApplicationProperties | it.water.core.api.bundle |
| SecurityContext | it.water.core.api.permission |
Core/Core-interceptors/src/main/java/it/water/core/interceptors/annotations/FrameworkComponent.java
Core/Core-interceptors/src/main/java/it/water/core/interceptors/annotations/Inject.java
Core/Core-api/src/main/java/it/water/core/api/interceptors/OnActivate.java
Core/Core-api/src/main/java/it/water/core/api/registry/ComponentRegistry.java
Core/Core-api/src/main/java/it/water/core/api/bundle/Runtime.java
(source root: Water Framework source repository root)
@FrameworkComponent(priority = 1) // 1 = lowest; use higher to override framework defaults
@Inject(injectOnceAtStartup = false) // false = dynamic re-injection; true = one-shot at startup
@OnActivate // method-level, no attributes — called when component registered
@OnDeactivate // method-level, no attributes — called when component unregistered
Water Framework implements a technology-agnostic runtime abstraction that allows the same application code to run on OSGi, Spring, and Quarkus through a unified ComponentRegistry and lifecycle model.
Application Components (@FrameworkComponent)
| annotated with lifecycle (@OnActivate, @OnDeactivate)
| injected via @Inject
v
ComponentRegistry (abstract service registry)
| register, find, unregister components
| invoke lifecycle methods
v
Runtime (thread-local security context + application properties)
|
+--- OSGi: OsgiComponentRegistry → BundleContext/ServiceRegistry
+--- Spring: SpringComponentRegistry → ApplicationContext/BeanFactory
+--- Test: TestComponentRegistry → In-memory HashMap
Runtime, ComponentRegistry, ComponentFilter, ComponentConfiguration, @OnActivate, @OnDeactivate (technology-agnostic interfaces)AbstractComponentRegistry base implementationWaterRuntime, RuntimeInitializer, ApplicationInitializer, AbstractInitializerWaterAbstractInterceptor, WaterComponentsInjector, @InjectOsgiComponentRegistry, OsgiServiceInterceptor, WaterBundleActivatorSpringComponentRegistry, SpringServiceInterceptor, BaseSpringInitializerTestComponentRegistry, WaterTestRuntime, TestRuntimeUtilsCore-api (interfaces: Runtime, ComponentRegistry, ComponentFilter, ComponentConfiguration)
|
+--- Core-registry (AbstractComponentRegistry, filter implementations)
| depends on: Core-api
|
+--- Core-interceptors (WaterAbstractInterceptor, WaterComponentsInjector, @Inject)
| depends on: Core-api
|
+--- Core-bundle (WaterRuntime, RuntimeInitializer, ApplicationInitializer, AbstractInitializer)
| depends on: Core-api, Core-registry, Core-interceptors
|
+--- Implementation-osgi (OsgiComponentRegistry, OsgiServiceInterceptor, WaterBundleActivator)
| depends on: Core-api, Core-registry, Core-bundle
|
+--- Implementation-spring (SpringComponentRegistry, SpringServiceInterceptor, BaseSpringInitializer)
| depends on: Core-api, Core-registry, Core-bundle
|
+--- Core-testing-utils (TestComponentRegistry, WaterTestRuntime, TestRuntimeUtils)
depends on: Core-api, Core-registry, Core-bundle
File: Core/Core-api/src/main/java/it/water/core/api/bundle/Runtime.java
public interface Runtime extends Service {
SecurityContext getSecurityContext();
void fillSecurityContext(SecurityContext securityContext);
ApplicationProperties getApplicationProperties();
}
| Method | Purpose |
|--------|---------|
| getSecurityContext() | Returns thread-local SecurityContext for current request |
| fillSecurityContext(ctx) | Sets thread-local security context for current thread |
| getApplicationProperties() | Returns application-wide configuration properties |
File: Core/Core-bundle/src/main/java/it/water/core/bundle/WaterRuntime.java
@FrameworkComponent(services = Runtime.class)
public class WaterRuntime implements Runtime {
private ThreadLocal<SecurityContext> securityContext = new ThreadLocal<>();
@Inject @Setter
private ApplicationProperties applicationProperties;
@Override
public SecurityContext getSecurityContext() {
return securityContext.get();
}
@Override
public void fillSecurityContext(SecurityContext ctx) {
securityContext.set(ctx);
}
@Override
public ApplicationProperties getApplicationProperties() {
return applicationProperties;
}
}
ThreadLocal<SecurityContext> for thread-safe per-request securityApplicationProperties at startupFile: Core/Core-api/src/main/java/it/water/core/api/registry/ComponentRegistry.java
package it.water.core.api.registry;
public interface ComponentRegistry {
// Find all components matching type and optional filter
<T> List<T> findComponents(Class<T> componentClass, ComponentFilter filter);
// Find single highest-priority component
<T> T findComponent(Class<T> componentClass, ComponentFilter filter);
// Register a new component
<T, K> ComponentRegistration<T, K> registerComponent(
Class<? extends T> componentClass,
T component,
ComponentConfiguration configuration);
// Unregister a component
<T> boolean unregisterComponent(ComponentRegistration<T, ?> registration);
<T> boolean unregisterComponent(Class<T> componentClass, T component);
// Filter builder access
ComponentFilterBuilder getComponentFilterBuilder();
// Entity-specific lookups
<T extends BaseEntitySystemApi> T findEntitySystemApi(String entityClassName);
<T extends BaseRepository> T findEntityRepository(String entityClassName);
<T extends BaseEntity> BaseRepository<T> findEntityExtensionRepository(Class<T> type);
}
findComponent(Class, filter) returns the component with the highest priority (lowest number)findComponents(Class, filter) returns all matching components, sorted by prioritynull (not an exception)| Priority Value | Meaning | |---------------|---------| | 0 | Highest priority (custom override) | | 1 | Default framework priority | | 2 | Lower priority (test/fallback) | | Higher numbers | Lower priority |
When multiple components implement the same interface, findComponent() returns the one with the lowest priority number.
File: Core/Core-registry/src/main/java/it/water/core/registry/AbstractComponentRegistry.java
Base implementation providing:
@OnActivate / @OnDeactivate method discoveryFile: Core/Core-interceptors/src/main/java/it/water/core/interceptors/annotations/FrameworkComponent.java (via ClassIndex)
package it.water.core.interceptors.annotations;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@IndexAnnotated // Atteo ClassIndex for compile-time discovery
public @interface FrameworkComponent {
String[] properties() default {}; // OSGi/Spring properties to expose
Class<?>[] services() default {}; // Interfaces to register as
int priority() default 1; // 1 = lowest priority, higher numbers = higher priority
}
// Register as specific service interface
@FrameworkComponent(services = MyService.class)
public class MyServiceImpl implements MyService { ... }
// Register as multiple interfaces
@FrameworkComponent(services = {MyService.class, AnotherService.class})
public class MultiServiceImpl implements MyService, AnotherService { ... }
// Register with custom priority (overrides default implementation)
@FrameworkComponent(services = MyService.class, priority = 0)
public class HighPriorityServiceImpl implements MyService { ... }
services parameter determines which interfaces the component is registered underservices is empty, the component is registered but not discoverable by interfacepriority determines resolution order when multiple implementations exist@IndexAnnotated ensures compile-time indexing (no classpath scanning at runtime)File: Core/Core-api/src/main/java/it/water/core/api/interceptors/OnActivate.java
package it.water.core.api.interceptors;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME)
public @interface OnActivate { } // no attributes
File: Core/Core-api/src/main/java/it/water/core/api/interceptors/OnDeactivate.java
package it.water.core.api.interceptors;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME)
public @interface OnDeactivate { } // no attributes
Component Instantiation (new MyServiceImpl())
|
v
Field Injection (@Inject fields resolved and set)
|
v
Registry Registration (component stored in registry)
|
v
@OnActivate method invoked <---- ACTIVATION
|
v
Component is live and serving requests
|
... (component lifetime) ...
|
v
@OnDeactivate method invoked <---- DEACTIVATION
|
v
Registry Unregistration
The framework automatically resolves method parameters from the ComponentRegistry:
@FrameworkComponent(services = MyService.class)
public class MyServiceImpl implements MyService {
// No-argument activation
@OnActivate
public void onActivate() {
log.info("Component activated");
}
// With parameter injection from registry
@OnActivate
public void onActivate(ComponentRegistry registry, ApplicationProperties props) {
// Parameters resolved at runtime from ComponentRegistry
this.someConfig = props.getProperty("my.config.key");
}
@OnDeactivate
public void onDeactivate() {
// Cleanup resources
this.cache.clear();
}
}
@OnActivate method signature for parameter typescomponentRegistry.findComponent(parameterType, null)@Inject fields ARE available during @OnActivate (they are injected before activation)@OnActivate method per class is invokednullFile: Core/Core-interceptors/src/main/java/it/water/core/interceptors/annotations/Inject.java
package it.water.core.interceptors.annotations;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@IndexAnnotated
public @interface Inject {
boolean injectOnceAtStartup() default false; // true = inject only once, not dynamically
}
| Mode | injectOnceAtStartup | Behavior |
|------|----------------------|----------|
| Dynamic (default) | false (default) | Re-inject on every method call |
| Startup | true | Inject once when component is registered, never again |
@FrameworkComponent(services = MyService.class)
public class MyServiceImpl implements MyService {
@Inject @Setter // Setter required for injection; default injectOnceAtStartup=false (re-injected per call)
private AnotherService anotherService;
@Inject @Setter
private ComponentRegistry componentRegistry;
@Inject(injectOnceAtStartup = true) @Setter // Inject only once at startup
private StableService stableService;
}
File: Core/Core-interceptors/src/main/java/it/water/core/interceptors/annotations/implementation/WaterComponentsInjector.java
@FrameworkComponent(services = WaterComponentsInjector.class)
public class WaterComponentsInjector implements BeforeMethodFieldInterceptor<Inject> {
public static <S extends Service> void inject(
ComponentRegistry componentRegistry, S destination, List<Field> fields) {
fields.forEach(annotatedField -> {
Object service = componentRegistry.findComponent(annotatedField.getType(), null);
if (service != null) {
Method setter = findSetterMethod(destination, annotatedField);
setter.invoke(destination, service);
}
});
}
}
myFieldsetMyField(FieldType value)@Setter for automatic setter generation1. @FrameworkComponent classes instantiated
2. @Inject fields with injectOnceAtStartup=true are resolved and set at startup
3. Components registered in ComponentRegistry
4. @OnActivate methods invoked
5. (On method calls) @Inject fields with injectOnceAtStartup=false (default) are re-injected
File: Core/Core-api/src/main/java/it/water/core/api/registry/filter/ComponentFilter.java
public interface ComponentFilter {
String getFilter();
ComponentFilter and(ComponentFilter filter);
ComponentFilter and(String propertyName, String propertyValue);
ComponentFilter or(ComponentFilter filter);
ComponentFilter or(String propertyName, String propertyValue);
ComponentFilter not();
boolean isNot();
boolean matches(Properties props);
}
File: Core/Core-api/src/main/java/it/water/core/api/registry/filter/ComponentFilterBuilder.java
public interface ComponentFilterBuilder {
ComponentFilter createFilter(String name, String value);
}
| Platform | Builder | Filter Format |
|----------|---------|---------------|
| OSGi | OSGiComponentFilterBuilder | LDAP syntax: (property=value) |
| Spring | SpringComponentFilterBuilder | Properties matching |
| Test | TestComponentFilterBuilder | In-memory matching |
ComponentFilterBuilder filterBuilder = componentRegistry
.findComponent(ComponentFilterBuilder.class, null);
// Simple filter
ComponentFilter filter = filterBuilder.createFilter("type", "sensor");
List<DeviceService> sensors = componentRegistry
.findComponents(DeviceService.class, filter);
// Composite filter
ComponentFilter filter = filterBuilder.createFilter("priority", "HIGH")
.and("status", "active")
.or(filterBuilder.createFilter("name", "legacy"));
// Find with filter
MyService service = componentRegistry.findComponent(MyService.class, filter);
File: Core/Core-api/src/main/java/it/water/core/api/registry/ComponentConfiguration.java
public interface ComponentConfiguration {
int getPriority();
boolean isPrimary();
Properties getConfiguration();
Dictionary<String, Object> getConfigurationAsDictionary();
void addProperty(String name, Object value);
void removeProperty(String name);
boolean hasProperty(String name);
}
ComponentConfiguration config = ComponentConfigurationFactory
.createNewComponentPropertyFactory()
.withPriority(2)
.addProperty("region", "eu-west")
.addProperty("maxRetries", 3)
.build();
// Register component with configuration
componentRegistry.registerComponent(
MyService.class,
new MyServiceImpl(),
config
);
// Find components with specific properties
ComponentFilter filter = filterBuilder.createFilter("region", "eu-west");
List<MyService> euServices = componentRegistry
.findComponents(MyService.class, filter);
File: Core/Core-api/src/main/java/it/water/core/api/registry/ApplicationConfiguration.java
public interface ApplicationConfiguration {
Properties getConfiguration();
}
public interface ApplicationProperties extends Service {
Object getProperty(String key);
void loadProperties(Properties props);
void override(String key, Object value);
boolean containsKey(String key);
}
File: Implementation/Implementation-osgi/.../OsgiApplicationConfiguration.java
public void loadProperties() {
Configuration config = confAdmin.getConfiguration("it.water.application");
Dictionary<String, Object> dict = config.getProperties();
// Convert to Properties
}
etc/it.water.application.cfg (in Karaf)File: Implementation/Implementation-spring/.../SpringApplicationConfiguration.java
public void loadProperties() {
for (PropertySource<?> source : environment.getPropertySources()) {
if (source instanceof EnumerablePropertySource) {
for (String name : ((EnumerablePropertySource<?>) source).getPropertyNames()) {
props.put(name, environment.getProperty(name));
}
}
}
}
application-{profile}.propertiesapplication.propertiesThe framework uses Atteo ClassIndex for compile-time annotation indexing:
@IndexAnnotated // On annotations like @FrameworkComponent, @FrameworkRestApi
At compile time, an annotation processor generates index files in META-INF/services/. At runtime, discovery is instant (no classpath scanning).
// In AbstractInitializer
protected void initializeFrameworkComponents() {
// Get all @FrameworkComponent-annotated classes from ClassIndex
Iterable<Class<?>> annotatedClasses = getAnnotatedClasses(FrameworkComponent.class);
this.setupFrameworkComponents(annotatedClasses);
}
| Aspect | ClassIndex | Classpath Scan | |--------|-----------|----------------| | Speed | O(1) lookup | O(n) classes | | Reflection | Zero at discovery | Full scan | | Cross-JAR | Works across JARs | May miss JARs | | Startup time | Milliseconds | Seconds |
File: Core/Core-bundle/src/main/java/it/water/core/bundle/AbstractInitializer.java
initializeFrameworkComponents()
|
v
getAnnotatedClasses(FrameworkComponent.class) -- ClassIndex lookup
|
v
setupFrameworkComponents(classes)
|
for each @FrameworkComponent class:
|
+-- 1. Instantiate via default constructor
|
+-- 2. Read @FrameworkComponent annotation (services, priority)
|
+-- 3. Create ComponentConfiguration (priority, properties)
|
+-- 4. Inject @Inject fields (injectOnceAtStartup=true)
| -> WaterComponentsInjector.inject(registry, component, fields)
|
+-- 5. Register component in ComponentRegistry
| -> registerComponent(serviceClass, component, config)
|
+-- 6. Invoke @OnActivate method
-> invokeLifecycleMethod(ACTIVATE, component)
File: Core/Core-bundle/src/main/java/it/water/core/bundle/ApplicationInitializer.java
Extends AbstractInitializer with additional application-level setup:
ApplicationInitializer.start()
|
+-- 1. initializeFrameworkComponents()
| (all @FrameworkComponent registration)
|
+-- 2.
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer