From 9212fb99b92c5b7feb0e02c41ed41b041ce84cb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:40:03 +0000 Subject: [PATCH 1/7] Initial plan From 8edf288eb94b7fe3796fcf89f21cb15ee661d477 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:50:08 +0000 Subject: [PATCH 2/7] Add JavaDoc comments with Example Usage sections to discovery client classes Add JavaDoc to non-private methods missing documentation in: - ReactiveDiscoveryClientAdapter: constructor, description(), getInstances(), getServices(), probe(), getOrder() - UnionDiscoveryClient: description(), getInstances(), getServices(), getDiscoveryClients(), getOrder(), afterSingletonsInstantiated(), destroy(), setApplicationContext() - DiscoveryClientAutoConfiguration: unionDiscoveryClient() bean method - ReactiveDiscoveryClientAutoConfiguration: reactiveDiscoveryClientAdapter() bean method Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ReactiveDiscoveryClientAdapter.java | 35 +++++++++++++++ .../discovery/UnionDiscoveryClient.java | 44 +++++++++++++++++++ .../DiscoveryClientAutoConfiguration.java | 13 ++++++ ...ctiveDiscoveryClientAutoConfiguration.java | 14 ++++++ 4 files changed, 106 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java index 652ca766..73ba2fe2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/ReactiveDiscoveryClientAdapter.java @@ -39,32 +39,67 @@ public class ReactiveDiscoveryClientAdapter implements DiscoveryClient { private final ReactiveDiscoveryClient reactiveDiscoveryClient; + /** + * Create a new {@link ReactiveDiscoveryClientAdapter} that wraps the given + * {@link ReactiveDiscoveryClient} as a blocking {@link DiscoveryClient}. + * + *

Example Usage: + *

{@code
+     * ReactiveDiscoveryClient reactiveClient = new SimpleReactiveDiscoveryClient(properties);
+     * DiscoveryClient adapter = new ReactiveDiscoveryClientAdapter(reactiveClient);
+     * List services = adapter.getServices();
+     * }
+ * + * @param reactiveDiscoveryClient the {@link ReactiveDiscoveryClient} to adapt, must not be {@code null} + */ public ReactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { this.reactiveDiscoveryClient = reactiveDiscoveryClient; } + /** + * {@inheritDoc} + *

Delegates to the underlying {@link ReactiveDiscoveryClient#description()}. + */ @Override public String description() { return this.reactiveDiscoveryClient.description(); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getInstances(String)} and collects the + * reactive {@link Flux} result into a blocking {@link List}. + */ @Override public List getInstances(String serviceId) { Flux flux = this.reactiveDiscoveryClient.getInstances(serviceId); return toList(flux); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getServices()} and collects the + * reactive {@link Flux} result into a blocking {@link List}. + */ @Override public List getServices() { Flux flux = this.reactiveDiscoveryClient.getServices(); return toList(flux); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#probe()}. + */ @Override public void probe() { this.reactiveDiscoveryClient.probe(); } + /** + * {@inheritDoc} + *

Delegates to {@link ReactiveDiscoveryClient#getOrder()}. + */ @Override public int getOrder() { return this.reactiveDiscoveryClient.getOrder(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java index 944f0cdd..8b19619c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/UnionDiscoveryClient.java @@ -48,11 +48,20 @@ public final class UnionDiscoveryClient implements DiscoveryClient, ApplicationC private List discoveryClients; + /** + * {@inheritDoc} + * + * @return the description "Union Discovery Client" + */ @Override public String description() { return "Union Discovery Client"; } + /** + * {@inheritDoc} + *

Aggregates service instances from all underlying {@link DiscoveryClient DiscoveryClients}. + */ @Override public List getInstances(String serviceId) { List serviceInstances = new LinkedList<>(); @@ -66,6 +75,10 @@ public List getInstances(String serviceId) { return serviceInstances; } + /** + * {@inheritDoc} + *

Returns a deduplicated union of service names from all underlying {@link DiscoveryClient DiscoveryClients}. + */ @Override public List getServices() { LinkedHashSet services = new LinkedHashSet<>(); @@ -79,6 +92,20 @@ public List getServices() { return new ArrayList<>(services); } + /** + * Returns the sorted list of underlying {@link DiscoveryClient DiscoveryClients}, excluding + * {@link CompositeDiscoveryClient} and this instance itself. The list is lazily initialized + * from the {@link ApplicationContext} on first access and cached for subsequent calls. + * + *

Example Usage: + *

{@code
+     * UnionDiscoveryClient unionClient = applicationContext.getBean(UnionDiscoveryClient.class);
+     * List clients = unionClient.getDiscoveryClients();
+     * clients.forEach(c -> System.out.println(c.description()));
+     * }
+ * + * @return an unmodifiable list of {@link DiscoveryClient} instances + */ public List getDiscoveryClients() { List discoveryClients = this.discoveryClients; if (discoveryClients != null) { @@ -98,21 +125,38 @@ public List getDiscoveryClients() { return discoveryClients; } + /** + * {@inheritDoc} + * + * @return {@link #HIGHEST_PRECEDENCE} to ensure this client takes priority + */ @Override public int getOrder() { return HIGHEST_PRECEDENCE; } + /** + * {@inheritDoc} + *

Eagerly initializes the list of {@link DiscoveryClient DiscoveryClients} after all singletons are instantiated. + */ @Override public void afterSingletonsInstantiated() { this.discoveryClients = getDiscoveryClients(); } + /** + * {@inheritDoc} + *

Clears the cached list of {@link DiscoveryClient DiscoveryClients} on bean destruction. + */ @Override public void destroy() throws Exception { this.discoveryClients.clear(); } + /** + * {@inheritDoc} + *

Stores the {@link ApplicationContext} used to look up {@link DiscoveryClient} beans. + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java index 28f9b1e7..7fc2c6eb 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/DiscoveryClientAutoConfiguration.java @@ -80,6 +80,19 @@ public class DiscoveryClientAutoConfiguration { @ConditionalOnProperty(name = DISCOVERY_CLIENT_MODE_PROPERTY_NAME, havingValue = UNION_DISCOVERY_CLIENT_MODE) public static class UnionConfiguration { + /** + * Creates a {@link UnionDiscoveryClient} bean that aggregates all {@link DiscoveryClient} + * instances in the {@link org.springframework.context.ApplicationContext}. + * + *

Example Usage: + *

{@code
+         * // Activated when microsphere.spring.cloud.client.discovery.mode=union
+         * UnionDiscoveryClient client = applicationContext.getBean(UnionDiscoveryClient.class);
+         * List services = client.getServices();
+         * }
+ * + * @return a new {@link UnionDiscoveryClient} instance + */ @Bean public UnionDiscoveryClient unionDiscoveryClient() { return new UnionDiscoveryClient(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java index 26aa7b18..8cb3a906 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/discovery/autoconfigure/ReactiveDiscoveryClientAutoConfiguration.java @@ -61,6 +61,20 @@ public class ReactiveDiscoveryClientAutoConfiguration { @ConditionalOnBlockingDiscoveryEnabled public static class BlockingConfiguration { + /** + * Creates a {@link ReactiveDiscoveryClientAdapter} bean that adapts a + * {@link ReactiveDiscoveryClient} to the blocking {@link org.springframework.cloud.client.discovery.DiscoveryClient} interface. + * + *

Example Usage: + *

{@code
+         * // Auto-configured when both reactive and blocking discovery are enabled
+         * DiscoveryClient client = applicationContext.getBean(ReactiveDiscoveryClientAdapter.class);
+         * List services = client.getServices();
+         * }
+ * + * @param reactiveDiscoveryClient the {@link ReactiveDiscoveryClient} to adapt + * @return a new {@link ReactiveDiscoveryClientAdapter} instance + */ @Bean @ConditionalOnBean(ReactiveDiscoveryClient.class) public ReactiveDiscoveryClientAdapter reactiveDiscoveryClientAdapter(ReactiveDiscoveryClient reactiveDiscoveryClient) { From 59def04f14a4f107e2b80d28d07f98fa9f70a859 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:55:31 +0000 Subject: [PATCH 3/7] Add JavaDoc comments with Example Usage sections to non-private methods Add comprehensive JavaDoc documentation to non-private methods that were missing it in the service registry package: - InMemoryServiceRegistry: all @Override methods and getMetadata() - MultipleAutoServiceRegistration: constructor and all @Override methods - MultipleRegistration: constructor, all @Override methods, getDefaultRegistration(), special() - MultipleServiceRegistry: constructor, all @Override methods, getRegistrationClass() - RegistrationMetaData: constructor and all Map @Override methods - SimpleAutoServiceRegistration: constructor and all @Override methods - SimpleServiceRegistry: all constructors, @Override methods, and package-private getInstances() Each JavaDoc includes: - Brief description with {@link} cross-references - Example Usage section with
{@code ...}
blocks - @param, @return tags where applicable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../registry/InMemoryServiceRegistry.java | 78 +++++++++ .../MultipleAutoServiceRegistration.java | 65 +++++++ .../registry/MultipleRegistration.java | 120 +++++++++++++ .../registry/MultipleServiceRegistry.java | 96 ++++++++++ .../registry/RegistrationMetaData.java | 164 ++++++++++++++++++ .../SimpleAutoServiceRegistration.java | 64 +++++++ .../registry/SimpleServiceRegistry.java | 126 ++++++++++++++ 7 files changed, 713 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java index 26da337e..7d1840c3 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/InMemoryServiceRegistry.java @@ -35,23 +35,73 @@ public class InMemoryServiceRegistry implements ServiceRegistry { private final ConcurrentMap storage = new ConcurrentHashMap<>(1); + /** + * Registers the given {@link Registration} instance in the in-memory storage, + * keyed by its instance ID. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * Registration registration = createRegistration();
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link Registration} to store + */ @Override public void register(Registration registration) { String id = registration.getInstanceId(); storage.put(id, registration); } + /** + * Removes the given {@link Registration} instance from the in-memory storage. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * Registration registration = createRegistration();
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link Registration} to remove + */ @Override public void deregister(Registration registration) { String id = registration.getInstanceId(); storage.remove(id, registration); } + /** + * Closes this registry by clearing all stored {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.close();
+     * }
+ */ @Override public void close() { storage.clear(); } + /** + * Sets the status of the given {@link Registration} by storing it in + * the registration's metadata under the {@code _status_} key. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link Registration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(Registration registration, String status) { Map metadata = getMetadata(registration); @@ -60,6 +110,20 @@ public void setStatus(Registration registration, String status) { } } + /** + * Retrieves the status of the given {@link Registration} from its metadata. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * Object status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link Registration} whose status is to be retrieved + * @return the status value, or {@code null} if not set or registration not found + */ @Override public Object getStatus(Registration registration) { Map metadata = getMetadata(registration); @@ -69,6 +133,20 @@ public Object getStatus(Registration registration) { return null; } + /** + * Retrieves the metadata {@link Map} for the given {@link Registration} + * from the in-memory storage. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry registry = new InMemoryServiceRegistry();
+     * registry.register(registration);
+     * Map metadata = registry.getMetadata(registration);
+     * }
+ * + * @param registration the {@link Registration} whose metadata is to be retrieved + * @return the metadata map, or {@code null} if the registration is not found + */ protected Map getMetadata(Registration registration) { String id = registration.getInstanceId(); Registration instance = storage.get(id); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java index 70b06231..227305f5 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleAutoServiceRegistration.java @@ -20,6 +20,24 @@ public class MultipleAutoServiceRegistration extends AbstractAutoServiceRegistra private final MultipleRegistration multipleRegistration; + /** + * Constructs a new {@link MultipleAutoServiceRegistration} with the specified + * {@link MultipleRegistration}, {@link ServiceRegistry}, and + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * ServiceRegistry serviceRegistry = new InMemoryServiceRegistry();
+     * AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties();
+     * MultipleAutoServiceRegistration autoReg =
+     *     new MultipleAutoServiceRegistration(registration, serviceRegistry, properties);
+     * }
+ * + * @param multipleRegistration the {@link MultipleRegistration} to manage + * @param serviceRegistry the {@link ServiceRegistry} to delegate to + * @param properties the {@link AutoServiceRegistrationProperties} for configuration + */ public MultipleAutoServiceRegistration(MultipleRegistration multipleRegistration, ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties) { @@ -28,21 +46,68 @@ public MultipleAutoServiceRegistration(MultipleRegistration multipleRegistration this.multipleRegistration = multipleRegistration; } + /** + * Returns the configuration object for this auto service registration. + * This implementation always returns {@code null}. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * Object config = autoReg.getConfiguration(); // null
+     * }
+ * + * @return {@code null} + */ @Override protected Object getConfiguration() { return null; } + /** + * Determines whether this auto service registration is enabled based on the + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * boolean enabled = autoReg.isEnabled();
+     * }
+ * + * @return {@code true} if auto service registration is enabled + */ @Override protected boolean isEnabled() { return this.autoServiceRegistrationProperties.isEnabled(); } + /** + * Returns the {@link MultipleRegistration} managed by this auto service registration. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * MultipleRegistration registration = autoReg.getRegistration();
+     * }
+ * + * @return the {@link MultipleRegistration} instance + */ @Override protected MultipleRegistration getRegistration() { return this.multipleRegistration; } + /** + * Returns the management {@link MultipleRegistration}, which is the same as + * the primary registration in this implementation. + * + *

Example Usage: + *

{@code
+     * MultipleAutoServiceRegistration autoReg = ...;
+     * MultipleRegistration mgmtRegistration = autoReg.getManagementRegistration();
+     * }
+ * + * @return the {@link MultipleRegistration} instance used for management + */ @Override protected MultipleRegistration getManagementRegistration() { return this.multipleRegistration; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java index 23783914..aba1ff63 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleRegistration.java @@ -29,6 +29,21 @@ public class MultipleRegistration implements Registration { private final RegistrationMetaData metaData; + /** + * Constructs a new {@link MultipleRegistration} from the given collection of + * {@link Registration} instances. The last registration in the collection becomes + * the default registration. + * + *

Example Usage: + *

{@code
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.setServiceId("test-service");
+     * MultipleRegistration multipleRegistration =
+     *     new MultipleRegistration(List.of(registration));
+     * }
+ * + * @param registrations the collection of {@link Registration} instances, must not be empty + */ public MultipleRegistration(Collection registrations) { assertNotEmpty(registrations, () -> "registrations cannot be empty"); //init map @@ -41,45 +56,150 @@ public MultipleRegistration(Collection registrations) { this.metaData = new RegistrationMetaData(registrations); } + /** + * Returns the instance ID from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String instanceId = multipleRegistration.getInstanceId();
+     * }
+ * + * @return the instance ID of the default registration + */ @Override public String getInstanceId() { return getDefaultRegistration().getInstanceId(); } + /** + * Returns the service ID from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String serviceId = multipleRegistration.getServiceId();
+     * }
+ * + * @return the service ID of the default registration + */ @Override public String getServiceId() { return getDefaultRegistration().getServiceId(); } + /** + * Returns the host from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * String host = multipleRegistration.getHost();
+     * }
+ * + * @return the host of the default registration + */ @Override public String getHost() { return getDefaultRegistration().getHost(); } + /** + * Returns the port from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * int port = multipleRegistration.getPort();
+     * }
+ * + * @return the port of the default registration + */ @Override public int getPort() { return getDefaultRegistration().getPort(); } + /** + * Returns whether the default {@link Registration} is secure. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * boolean secure = multipleRegistration.isSecure();
+     * }
+ * + * @return {@code true} if the default registration is secure + */ @Override public boolean isSecure() { return getDefaultRegistration().isSecure(); } + /** + * Returns the {@link URI} from the default {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * URI uri = multipleRegistration.getUri();
+     * }
+ * + * @return the URI of the default registration + */ @Override public URI getUri() { return getDefaultRegistration().getUri(); } + /** + * Returns the aggregated {@link RegistrationMetaData} that synchronizes metadata + * across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * Map metadata = multipleRegistration.getMetadata();
+     * }
+ * + * @return the aggregated metadata map + */ @Override public Map getMetadata() { return metaData; } + /** + * Returns the default {@link Registration} instance, which is the last registration + * provided during construction. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * Registration defaultReg = multipleRegistration.getDefaultRegistration();
+     * }
+ * + * @return the default {@link Registration} + */ public Registration getDefaultRegistration() { return defaultRegistration; } + /** + * Retrieves a specific {@link Registration} by its class type. If the specified + * class is {@link Registration} itself, returns this {@link MultipleRegistration}. + * + *

Example Usage: + *

{@code
+     * MultipleRegistration multipleRegistration = new MultipleRegistration(registrations);
+     * DefaultRegistration specific = multipleRegistration.special(DefaultRegistration.class);
+     * Registration self = multipleRegistration.special(Registration.class);
+     * }
+ * + * @param specialClass the specific {@link Registration} subclass to look up + * @param the type of the registration + * @return the matching registration, or {@code null} if not found + */ public T special(Class specialClass) { if (Registration.class.equals(specialClass)) return (T) this; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java index 4b5aa292..d87c3118 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/MultipleServiceRegistry.java @@ -37,6 +37,21 @@ public class MultipleServiceRegistry implements ServiceRegistryExample Usage: + *
{@code
+     * ServiceRegistry simpleRegistry = new InMemoryServiceRegistry();
+     * MultipleServiceRegistry registry =
+     *     new MultipleServiceRegistry(Map.of("default", simpleRegistry));
+     * }
+ * + * @param registriesMap the map of Spring bean names to {@link ServiceRegistry} instances, + * must not be empty + */ public MultipleServiceRegistry(Map registriesMap) { assertNotEmpty(registriesMap, () -> "registrations cannot be empty"); @@ -53,21 +68,71 @@ public MultipleServiceRegistry(Map registriesMap) { } } + /** + * Registers the given {@link MultipleRegistration} by delegating to each underlying + * {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link MultipleRegistration} to register + */ @Override public void register(MultipleRegistration registration) { iterate(registration, (reg, registry) -> registry.register(reg)); } + /** + * Deregisters the given {@link MultipleRegistration} by delegating to each underlying + * {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * MultipleRegistration registration = new MultipleRegistration(registrations);
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link MultipleRegistration} to deregister + */ @Override public void deregister(MultipleRegistration registration) { iterate(registration, (reg, registry) -> registry.deregister(reg)); } + /** + * Closes all underlying {@link ServiceRegistry} instances. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.close();
+     * }
+ */ @Override public void close() { iterate(ServiceRegistry::close); } + /** + * Sets the status of the given {@link MultipleRegistration} by delegating to each + * underlying {@link ServiceRegistry} with the corresponding specific {@link Registration}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link MultipleRegistration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(MultipleRegistration registration, String status) { iterate(registration, (reg, registry) -> registry.setStatus(reg, status)); @@ -87,6 +152,22 @@ private void iterate(Consumer action) { registriesMap.values().forEach(action); } + /** + * Retrieves the status of the given {@link MultipleRegistration} from the default + * {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * MultipleServiceRegistry registry = new MultipleServiceRegistry(registriesMap);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * Object status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link MultipleRegistration} whose status is to be retrieved + * @param the type of the status value + * @return the status from the default service registry + */ @Override public T getStatus(MultipleRegistration registration) { Class registrationClass = beanNameToRegistrationTypesMap.get(defaultRegistrationBeanName); @@ -94,6 +175,21 @@ public T getStatus(MultipleRegistration registration) { return (T) defaultServiceRegistry.getStatus(targetRegistration); } + /** + * Resolves the {@link Registration} class for the given {@link ServiceRegistry} class + * using generic type resolution. Falls back to {@code SpringFactoriesLoader} when the + * generic type resolves to {@link Registration} itself. + * + *

Example Usage: + *

{@code
+     * Class regClass =
+     *     MultipleServiceRegistry.getRegistrationClass(NacosServiceRegistry.class);
+     * // returns NacosRegistration.class
+     * }
+ * + * @param serviceRegistryClass the {@link ServiceRegistry} implementation class + * @return the resolved {@link Registration} subclass + */ static Class getRegistrationClass(Class serviceRegistryClass) { Class registrationClass = forClass(serviceRegistryClass) .as(ServiceRegistry.class) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java index 6cab1bc0..638acce7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/RegistrationMetaData.java @@ -40,6 +40,20 @@ public final class RegistrationMetaData implements Map { private final Object lock = new Object(); + /** + * Constructs a new {@link RegistrationMetaData} that aggregates metadata from the given + * collection of {@link Registration} instances. Metadata changes are synchronized across + * all registrations. + * + *

Example Usage: + *

{@code
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.getMetadata().put("key1", "value1");
+     * RegistrationMetaData metaData = new RegistrationMetaData(List.of(registration));
+     * }
+ * + * @param registrations the collection of {@link Registration} instances, must not be empty + */ public RegistrationMetaData(Collection registrations) { assertNotEmpty(registrations, () -> "registrations cannot be empty"); this.registrations = registrations; @@ -59,31 +73,107 @@ public RegistrationMetaData(Collection registrations) { } } + /** + * Returns the number of metadata entries. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * int count = metaData.size(); // e.g. 3
+     * }
+ * + * @return the number of key-value mappings in this metadata + */ @Override public int size() { return applicationMetaData.size(); } + /** + * Returns whether this metadata map is empty. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean empty = metaData.isEmpty(); // false if registrations have metadata
+     * }
+ * + * @return {@code true} if this metadata contains no entries + */ @Override public boolean isEmpty() { return this.applicationMetaData.isEmpty(); } + /** + * Returns whether this metadata contains the specified key. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean hasKey = metaData.containsKey("key1"); // true
+     * boolean missing = metaData.containsKey("unknown"); // false
+     * }
+ * + * @param key the key to check for + * @return {@code true} if this metadata contains the specified key + */ @Override public boolean containsKey(Object key) { return this.applicationMetaData.containsKey(key); } + /** + * Returns whether this metadata contains the specified value. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * boolean hasValue = metaData.containsValue("value1"); // true
+     * boolean missing = metaData.containsValue("unknown"); // false
+     * }
+ * + * @param value the value to check for + * @return {@code true} if this metadata contains the specified value + */ @Override public boolean containsValue(Object value) { return this.applicationMetaData.containsValue(value); } + /** + * Returns the metadata value associated with the specified key. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * String value = metaData.get("key1"); // "value1"
+     * String missing = metaData.get("unknown"); // null
+     * }
+ * + * @param key the key whose associated value is to be returned + * @return the value associated with the key, or {@code null} if not found + */ @Override public String get(Object key) { return this.applicationMetaData.get(key); } + /** + * Puts a metadata entry and synchronizes it across all underlying {@link Registration} + * instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.put("key4", "value4");
+     * String value = metaData.get("key4"); // "value4"
+     * }
+ * + * @param key the metadata key + * @param value the metadata value + * @return the previous value associated with the key, or {@code null} + */ @Override public String put(String key, String value) { synchronized (lock) { @@ -94,6 +184,20 @@ public String put(String key, String value) { return this.applicationMetaData.put(key, value); } + /** + * Removes the metadata entry for the specified key and synchronizes the removal + * across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.remove("key1");
+     * String value = metaData.get("key1"); // null
+     * }
+ * + * @param key the key whose mapping is to be removed + * @return the previous value associated with the key, or {@code null} + */ @Override public String remove(Object key) { synchronized (lock) { @@ -104,6 +208,18 @@ public String remove(Object key) { return this.applicationMetaData.remove(key); } + /** + * Copies all entries from the specified map into this metadata and synchronizes + * them across all underlying {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.putAll(Map.of("key4", "value4", "key5", "value5"));
+     * }
+ * + * @param m the map of entries to add + */ @Override public void putAll(Map m) { synchronized (lock) { @@ -114,6 +230,17 @@ public void putAll(Map m) { this.applicationMetaData.putAll(m); } + /** + * Clears all metadata entries and synchronizes the clearing across all underlying + * {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * metaData.clear();
+     * int size = metaData.size(); // 0
+     * }
+ */ @Override public void clear() { synchronized (lock) { @@ -122,16 +249,53 @@ public void clear() { this.applicationMetaData.clear(); } + /** + * Returns an unmodifiable {@link Set} view of the metadata keys. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Set keys = metaData.keySet();
+     * boolean hasKey = keys.contains("key1"); // true
+     * }
+ * + * @return an unmodifiable set of metadata keys + */ @Override public Set keySet() { return Collections.unmodifiableSet(this.applicationMetaData.keySet()); } + /** + * Returns an unmodifiable {@link Collection} view of the metadata values. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Collection values = metaData.values();
+     * boolean hasValue = values.contains("value1"); // true
+     * }
+ * + * @return an unmodifiable collection of metadata values + */ @Override public Collection values() { return Collections.unmodifiableCollection(this.applicationMetaData.values()); } + /** + * Returns a modifiable {@link Set} view of the metadata entries. Unlike + * {@link #keySet()} and {@link #values()}, the returned set is not wrapped + * in an unmodifiable view. + * + *

Example Usage: + *

{@code
+     * RegistrationMetaData metaData = new RegistrationMetaData(registrations);
+     * Set> entries = metaData.entrySet();
+     * }
+ * + * @return a set of metadata entries + */ @Override public Set> entrySet() { return this.applicationMetaData.entrySet(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java index c0409a1b..26c3c4d2 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleAutoServiceRegistration.java @@ -34,6 +34,24 @@ public class SimpleAutoServiceRegistration extends AbstractAutoServiceRegistrati private final Registration registration; + /** + * Constructs a new {@link SimpleAutoServiceRegistration} with the specified + * {@link ServiceRegistry}, {@link AutoServiceRegistrationProperties}, and + * {@link Registration}. + * + *

Example Usage: + *

{@code
+     * InMemoryServiceRegistry serviceRegistry = new InMemoryServiceRegistry();
+     * AutoServiceRegistrationProperties properties = new AutoServiceRegistrationProperties();
+     * Registration registration = createRegistration();
+     * SimpleAutoServiceRegistration autoReg =
+     *     new SimpleAutoServiceRegistration(serviceRegistry, properties, registration);
+     * }
+ * + * @param serviceRegistry the {@link ServiceRegistry} to delegate to + * @param properties the {@link AutoServiceRegistrationProperties} for configuration + * @param registration the {@link Registration} to manage + */ public SimpleAutoServiceRegistration(ServiceRegistry serviceRegistry, AutoServiceRegistrationProperties properties, Registration registration) { super(serviceRegistry, properties); @@ -41,21 +59,67 @@ public SimpleAutoServiceRegistration(ServiceRegistry serviceRegist this.registration = registration; } + /** + * Returns the {@link AutoServiceRegistrationProperties} as the configuration object. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Object config = autoReg.getConfiguration();
+     * }
+ * + * @return the {@link AutoServiceRegistrationProperties} instance + */ @Override protected Object getConfiguration() { return properties; } + /** + * Determines whether this auto service registration is enabled based on the + * {@link AutoServiceRegistrationProperties}. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * boolean enabled = autoReg.isEnabled();
+     * }
+ * + * @return {@code true} if auto service registration is enabled + */ @Override protected boolean isEnabled() { return properties.isEnabled(); } + /** + * Returns the {@link Registration} managed by this auto service registration. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Registration registration = autoReg.getRegistration();
+     * }
+ * + * @return the {@link Registration} instance + */ @Override protected Registration getRegistration() { return registration; } + /** + * Returns the management {@link Registration}, which is the same as the primary + * registration in this implementation. + * + *

Example Usage: + *

{@code
+     * SimpleAutoServiceRegistration autoReg = ...;
+     * Registration mgmtRegistration = autoReg.getManagementRegistration();
+     * }
+ * + * @return the {@link Registration} instance used for management + */ @Override protected Registration getManagementRegistration() { return registration; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java index 6d90e12b..386bd33c 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/SimpleServiceRegistry.java @@ -48,48 +48,174 @@ public class SimpleServiceRegistry implements ServiceRegistry> instancesMap; + /** + * Constructs a new {@link SimpleServiceRegistry} using the given + * {@link SimpleDiscoveryProperties} to obtain the instances map. + * + *

Example Usage: + *

{@code
+     * SimpleDiscoveryProperties properties = new SimpleDiscoveryProperties();
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * }
+ * + * @param properties the {@link SimpleDiscoveryProperties} to use + */ public SimpleServiceRegistry(SimpleDiscoveryProperties properties) { this(getInstancesMap(properties)); } + /** + * Constructs a new {@link SimpleServiceRegistry} using the given + * {@link SimpleReactiveDiscoveryProperties} to obtain the instances map. + * + *

Example Usage: + *

{@code
+     * SimpleReactiveDiscoveryProperties reactiveProperties = ...;
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(reactiveProperties);
+     * }
+ * + * @param properties the {@link SimpleReactiveDiscoveryProperties} to use + */ public SimpleServiceRegistry(SimpleReactiveDiscoveryProperties properties) { this(getInstancesMap(properties)); } + /** + * Constructs a new {@link SimpleServiceRegistry} with the given instances map. + * + *

Example Usage: + *

{@code
+     * Map> instancesMap = new HashMap<>();
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(instancesMap);
+     * }
+ * + * @param instancesMap the map of service IDs to {@link DefaultServiceInstance} lists + */ public SimpleServiceRegistry(Map> instancesMap) { this.instancesMap = instancesMap; } + /** + * Registers the given {@link DefaultRegistration} by adding it to the instances list + * for its service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * DefaultRegistration registration = new DefaultRegistration();
+     * registration.setServiceId("test-service");
+     * registry.register(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to register + */ @Override public void register(DefaultRegistration registration) { List instances = getInstances(registration); instances.add(registration); } + /** + * Deregisters the given {@link DefaultRegistration} by removing it from the instances + * list for its service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.deregister(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to deregister + */ @Override public void deregister(DefaultRegistration registration) { List instances = getInstances(registration); instances.remove(registration); } + /** + * Closes this registry. This implementation is a no-op. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.close();
+     * }
+ */ @Override public void close() { } + /** + * Sets the status of the given {@link DefaultRegistration} by storing it in the + * registration's metadata under the {@link #STATUS_KEY} key. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * }
+ * + * @param registration the {@link DefaultRegistration} whose status is to be set + * @param status the status value to set + */ @Override public void setStatus(DefaultRegistration registration, String status) { setMetadata(registration, STATUS_KEY, status); } + /** + * Retrieves the status of the given {@link DefaultRegistration} from its metadata. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * registry.setStatus(registration, "UP");
+     * String status = registry.getStatus(registration); // "UP"
+     * }
+ * + * @param registration the {@link DefaultRegistration} whose status is to be retrieved + * @return the status value, or {@code null} if not set + */ @Override public String getStatus(DefaultRegistration registration) { return getMetadata(registration, STATUS_KEY); } + /** + * Returns the list of {@link DefaultServiceInstance} instances for the given + * {@link DefaultRegistration}'s service ID. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * registry.register(registration);
+     * List instances = registry.getInstances(registration);
+     * }
+ * + * @param registration the {@link DefaultRegistration} to look up instances for + * @return the list of instances for the registration's service ID + */ List getInstances(DefaultRegistration registration) { return getInstances(registration.getServiceId()); } + /** + * Returns the list of {@link DefaultServiceInstance} instances for the given service ID, + * creating an empty list if none exists. + * + *

Example Usage: + *

{@code
+     * SimpleServiceRegistry registry = new SimpleServiceRegistry(properties);
+     * List instances = registry.getInstances("test-service");
+     * }
+ * + * @param serviceId the service ID to look up + * @return the list of instances for the service ID + */ List getInstances(String serviceId) { return this.instancesMap.computeIfAbsent(serviceId, k -> new ArrayList<>()); } From ab3719d3f22f742f5a2fc30eea1c53fe839d9081 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:59:59 +0000 Subject: [PATCH 4/7] Add JavaDoc with Example Usage sections to non-private methods Add comprehensive JavaDoc comments with Example Usage sections using
{@code ...}
blocks to all non-private methods that were missing documentation across 10 service registry related files. Methods documented include @Bean factory methods, AOP advices, lifecycle callbacks, actuator endpoint operations, and protected template methods. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...RegistrationEndpointAutoConfiguration.java | 26 ++++++++ .../EventPublishingRegistrationAspect.java | 65 +++++++++++++++++++ .../ServiceRegistryAutoConfiguration.java | 46 +++++++++++++ ...oServiceRegistrationAutoConfiguration.java | 29 +++++++++ ...bFluxServiceRegistryAutoConfiguration.java | 26 ++++++++ ...ebMvcServiceRegistryAutoConfiguration.java | 26 ++++++++ .../WebServiceRegistryAutoConfiguration.java | 13 ++++ .../AbstractServiceRegistrationEndpoint.java | 58 +++++++++++++++++ .../ServiceDeregistrationEndpoint.java | 13 ++++ .../endpoint/ServiceRegistrationEndpoint.java | 28 ++++++++ 10 files changed, 330 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java index 3b336488..531fc200 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/actuate/autoconfigure/ServiceRegistrationEndpointAutoConfiguration.java @@ -49,6 +49,19 @@ }) public class ServiceRegistrationEndpointAutoConfiguration { + /** + * Creates a {@link ServiceRegistrationEndpoint} bean for managing service registration via actuator. + * + *

Example Usage: + *

{@code
+     * // The endpoint is auto-configured and accessible at /actuator/serviceRegistration
+     * @Autowired
+     * ServiceRegistrationEndpoint endpoint;
+     * Map metadata = endpoint.metadata();
+     * }
+ * + * @return a new {@link ServiceRegistrationEndpoint} instance + */ @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint @@ -56,6 +69,19 @@ public ServiceRegistrationEndpoint serviceRegistrationEndpoint() { return new ServiceRegistrationEndpoint(); } + /** + * Creates a {@link ServiceDeregistrationEndpoint} bean for managing service deregistration via actuator. + * + *

Example Usage: + *

{@code
+     * // The endpoint is auto-configured and accessible at /actuator/serviceDeregistration
+     * @Autowired
+     * ServiceDeregistrationEndpoint endpoint;
+     * boolean wasRunning = endpoint.stop();
+     * }
+ * + * @return a new {@link ServiceDeregistrationEndpoint} instance + */ @Bean @ConditionalOnMissingBean @ConditionalOnAvailableEndpoint diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java index de785f4b..0c99526a 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/aspect/EventPublishingRegistrationAspect.java @@ -59,6 +59,20 @@ public class EventPublishingRegistrationAspect implements ApplicationContextAwar private ObjectProvider registrationCustomizers; + /** + * AOP advice executed before {@link ServiceRegistry#register(Registration)}, publishing a + * {@link RegistrationPreRegisteredEvent} and applying {@link RegistrationCustomizer customizations}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically when ServiceRegistry.register() is called:
+     * serviceRegistry.register(registration);
+     * // A RegistrationPreRegisteredEvent is published before actual registration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} being registered + */ @Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeRegister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -70,6 +84,20 @@ public void beforeRegister(ServiceRegistry registry, Registration registration) }); } + /** + * AOP advice executed before {@link ServiceRegistry#deregister(Registration)}, publishing a + * {@link RegistrationPreDeregisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically when ServiceRegistry.deregister() is called:
+     * serviceRegistry.deregister(registration);
+     * // A RegistrationPreDeregisteredEvent is published before actual deregistration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} being deregistered + */ @Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void beforeDeregister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -78,6 +106,20 @@ public void beforeDeregister(ServiceRegistry registry, Registration registration context.publishEvent(new RegistrationPreDeregisteredEvent(registry, registration)); } + /** + * AOP advice executed after {@link ServiceRegistry#register(Registration)}, publishing a + * {@link RegistrationRegisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically after ServiceRegistry.register() completes:
+     * serviceRegistry.register(registration);
+     * // A RegistrationRegisteredEvent is published after successful registration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} that was registered + */ @After(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterRegister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -86,6 +128,20 @@ public void afterRegister(ServiceRegistry registry, Registration registration) { context.publishEvent(new RegistrationRegisteredEvent(registry, registration)); } + /** + * AOP advice executed after {@link ServiceRegistry#deregister(Registration)}, publishing a + * {@link RegistrationDeregisteredEvent}. + * + *

Example Usage: + *

{@code
+     * // This advice is triggered automatically after ServiceRegistry.deregister() completes:
+     * serviceRegistry.deregister(registration);
+     * // A RegistrationDeregisteredEvent is published after successful deregistration
+     * }
+ * + * @param registry the target {@link ServiceRegistry} + * @param registration the {@link Registration} that was deregistered + */ @After(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration") public void afterDeregister(ServiceRegistry registry, Registration registration) { if (isIgnored(registry)) { @@ -98,6 +154,15 @@ boolean isIgnored(ServiceRegistry registry) { return MultipleServiceRegistry.class.isAssignableFrom(registry.getClass()); } + /** + * {@inheritDoc} + * + *

Example Usage: + *

{@code
+     * EventPublishingRegistrationAspect aspect = new EventPublishingRegistrationAspect();
+     * aspect.setApplicationContext(applicationContext);
+     * }
+ */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java index ba838d6d..9eb53ecc 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/ServiceRegistryAutoConfiguration.java @@ -55,6 +55,20 @@ public class ServiceRegistryAutoConfiguration { @ConditionalOnMultipleRegistrationEnabled static class MultipleConfiguration { + /** + * Creates a primary {@link MultipleRegistration} bean that aggregates all available + * {@link Registration} instances. + * + *

Example Usage: + *

{@code
+         * @Autowired
+         * MultipleRegistration multipleRegistration;
+         * // Access individual registrations from the composite
+         * }
+ * + * @param registrations the collection of {@link Registration} instances + * @return a new {@link MultipleRegistration} aggregating the provided registrations + */ @Primary @Bean @ConditionalOnMissingBean @@ -62,6 +76,21 @@ public MultipleRegistration multipleRegistration(Collection regist return new MultipleRegistration(registrations); } + /** + * Creates a primary {@link MultipleServiceRegistry} bean that delegates to all available + * {@link ServiceRegistry} instances. + * + *

Example Usage: + *

{@code
+         * @Autowired
+         * MultipleServiceRegistry multipleServiceRegistry;
+         * // Register with all service registries at once
+         * multipleServiceRegistry.register(registration);
+         * }
+ * + * @param registriesMap a map of bean names to {@link ServiceRegistry} instances + * @return a new {@link MultipleServiceRegistry} delegating to all registries + */ @Bean @Primary @ConditionalOnMissingBean @@ -69,6 +98,23 @@ public MultipleServiceRegistry multipleServiceRegistry(MapExample Usage: + *
{@code
+         * @Autowired
+         * MultipleAutoServiceRegistration autoRegistration;
+         * // Auto-registration is managed by the Spring lifecycle
+         * boolean running = autoRegistration.isRunning();
+         * }
+ * + * @param multipleRegistration the composite {@link MultipleRegistration} + * @param multipleServiceRegistry the composite {@link MultipleServiceRegistry} + * @param properties the {@link AutoServiceRegistrationProperties} + * @return a new {@link MultipleAutoServiceRegistration} instance + */ @ConditionalOnBean(AutoServiceRegistrationProperties.class) @Primary @Bean diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java index 7453eef1..c3028236 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/SimpleAutoServiceRegistrationAutoConfiguration.java @@ -80,6 +80,22 @@ public class SimpleAutoServiceRegistrationAutoConfiguration { ) public static final String ENABLED_PROPERTY_NAME = PROPERTY_NAME_PREFIX + PropertyConstants.ENABLED_PROPERTY_NAME; + /** + * Creates a {@link Registration} bean from the application name, server properties, and network info. + * + *

Example Usage: + *

{@code
+     * // Auto-configured via Spring Boot; the bean is available for injection:
+     * @Autowired
+     * Registration registration;
+     * String serviceId = registration.getServiceId();
+     * }
+ * + * @param applicationName the Spring application name resolved from {@code spring.application.name} + * @param serverProperties the {@link ServerProperties} providing the server port + * @param inetUtils the {@link InetUtils} for resolving the host address + * @return a new {@link DefaultRegistration} instance + */ @Bean public Registration registration( @Value("${spring.application.name:default}") String applicationName, @@ -98,6 +114,19 @@ public Registration registration( return registration; } + /** + * Creates an {@link InMemoryServiceRegistry} bean as the default {@link ServiceRegistry} implementation. + * + *

Example Usage: + *

{@code
+     * // Auto-configured when no other ServiceRegistry bean is present:
+     * @Autowired
+     * ServiceRegistry serviceRegistry;
+     * serviceRegistry.register(registration);
+     * }
+ * + * @return a new {@link InMemoryServiceRegistry} instance + */ @Bean @ConditionalOnMissingBean public ServiceRegistry serviceRegistry() { diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java index 4dbdff1e..87ede268 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebFluxServiceRegistryAutoConfiguration.java @@ -31,11 +31,37 @@ @ConditionalOnWebApplication(type = REACTIVE) public class WebFluxServiceRegistryAutoConfiguration extends WebServiceRegistryAutoConfiguration { + /** + * {@inheritDoc} + *

Returns an empty string as WebFlux applications do not use a servlet context path. + * + *

Example Usage: + *

{@code
+     * WebFluxServiceRegistryAutoConfiguration config = new WebFluxServiceRegistryAutoConfiguration();
+     * String contextPath = config.getContextPath(); // returns ""
+     * }
+ * + * @return an empty string + */ @Override protected String getContextPath() { return ""; } + /** + * {@inheritDoc} + *

Always returns {@code false} for WebFlux applications, as no mappings are excluded. + * + *

Example Usage: + *

{@code
+     * WebFluxServiceRegistryAutoConfiguration config = new WebFluxServiceRegistryAutoConfiguration();
+     * boolean excluded = config.isExcludedMapping(mapping, patterns); // always false
+     * }
+ * + * @param mapping the {@link WebEndpointMapping} to evaluate + * @param patterns the URL patterns associated with the mapping + * @return always {@code false} + */ @Override protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { return false; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java index 1a8fc0d7..b160d563 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebMvcServiceRegistryAutoConfiguration.java @@ -55,11 +55,37 @@ public class WebMvcServiceRegistryAutoConfiguration extends WebServiceRegistryAu @Autowired private ObjectProvider dispatcherServletRegistrationBeanProvider; + /** + * {@inheritDoc} + *

Returns the servlet context path configured via {@code server.servlet.context-path}. + * + *

Example Usage: + *

{@code
+     * // With application property: server.servlet.context-path=/api
+     * String contextPath = config.getContextPath(); // returns "/api"
+     * }
+ * + * @return the servlet context path + */ @Override protected String getContextPath() { return this.contextPath; } + /** + * {@inheritDoc} + *

Excludes built-in Spring filter mappings and the default DispatcherServlet mapping. + * + *

Example Usage: + *

{@code
+     * boolean excluded = config.isExcludedMapping(mapping, new String[]{"/*"});
+     * // returns true if the mapping matches a built-in filter or DispatcherServlet
+     * }
+ * + * @param mapping the {@link WebEndpointMapping} to evaluate + * @param patterns the URL patterns associated with the mapping + * @return {@code true} if the mapping is a built-in filter or DispatcherServlet mapping + */ @Override protected boolean isExcludedMapping(WebEndpointMapping mapping, String[] patterns) { return isBuiltInFilterMapping(patterns) || isDispatcherServletMapping(mapping, patterns); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java index e025a791..02f61c23 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/autoconfigure/WebServiceRegistryAutoConfiguration.java @@ -62,6 +62,19 @@ public abstract class WebServiceRegistryAutoConfiguration implements Application @Value("${management.endpoints.web.base-path:/actuator}") protected String actuatorBasePath; + /** + * Handles {@link WebEndpointMappingsReadyEvent} by attaching web endpoint mapping metadata + * to all available {@link Registration} instances. + * + *

Example Usage: + *

{@code
+     * // This listener is invoked automatically by the Spring event system:
+     * // When WebEndpointMappingsReadyEvent is published, metadata is attached
+     * // to each Registration bean in the ApplicationContext.
+     * }
+ * + * @param event the {@link WebEndpointMappingsReadyEvent} containing the web endpoint mappings + */ @Override public final void onApplicationEvent(WebEndpointMappingsReadyEvent event) { ApplicationContext context = event.getApplicationContext(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java index 5c25b8bb..c9737de9 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/AbstractServiceRegistrationEndpoint.java @@ -46,6 +46,17 @@ public abstract class AbstractServiceRegistrationEndpoint implements SmartInitia protected static boolean running; + /** + * {@inheritDoc} + *

Initializes the {@link Registration}, {@link ServiceRegistry}, and + * {@link AbstractAutoServiceRegistration} from available bean providers. + * + *

Example Usage: + *

{@code
+     * // Called automatically by the Spring container after all singletons are instantiated.
+     * // Ensures registration, serviceRegistry, and serviceRegistration fields are populated.
+     * }
+ */ @Override public void afterSingletonsInstantiated() { this.registration = registrationProvider.getIfAvailable(); @@ -53,6 +64,19 @@ public void afterSingletonsInstantiated() { this.serviceRegistration = autoServiceRegistrationProvider.getIfAvailable(); } + /** + * {@inheritDoc} + *

Captures the web server port and detects the running state of the + * {@link AbstractAutoServiceRegistration}. + * + *

Example Usage: + *

{@code
+     * // Called automatically when the embedded web server has been initialized.
+     * // After this event, the port and running state are available.
+     * }
+ * + * @param event the {@link WebServerInitializedEvent} carrying the initialized web server + */ @Override public void onApplicationEvent(WebServerInitializedEvent event) { WebServer webServer = event.getWebServer(); @@ -60,14 +84,48 @@ public void onApplicationEvent(WebServerInitializedEvent event) { this.running = detectRunning(serviceRegistration); } + /** + * Detects whether the given {@link AbstractAutoServiceRegistration} is currently running. + * + *

Example Usage: + *

{@code
+     * boolean running = AbstractServiceRegistrationEndpoint.detectRunning(serviceRegistration);
+     * }
+ * + * @param serviceRegistration the {@link AbstractAutoServiceRegistration} to check, may be {@code null} + * @return {@code true} if the service registration is running, {@code false} otherwise + */ static boolean detectRunning(AbstractAutoServiceRegistration serviceRegistration) { return serviceRegistration == null ? false : serviceRegistration.isRunning(); } + /** + * Returns whether the service registration is currently running. + * + *

Example Usage: + *

{@code
+     * if (endpoint.isRunning()) {
+     *     // service is registered and running
+     * }
+     * }
+ * + * @return {@code true} if the service registration is running, {@code false} otherwise + */ protected boolean isRunning() { return running; } + /** + * Sets the running state of the service registration. + * + *

Example Usage: + *

{@code
+     * endpoint.setRunning(true);  // mark service as running
+     * endpoint.setRunning(false); // mark service as stopped
+     * }
+ * + * @param running {@code true} to mark the service as running, {@code false} otherwise + */ public void setRunning(boolean running) { this.running = running; } diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java index a3a5e418..e9858f1d 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceDeregistrationEndpoint.java @@ -15,6 +15,19 @@ @Endpoint(id = "serviceDeregistration") public class ServiceDeregistrationEndpoint extends AbstractServiceRegistrationEndpoint { + /** + * Deregisters the service from the {@link ServiceRegistry} if it is currently running. + * This is a write operation exposed via the {@code /actuator/serviceDeregistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP POST to /actuator/serviceDeregistration
+     * ServiceDeregistrationEndpoint endpoint = context.getBean(ServiceDeregistrationEndpoint.class);
+     * boolean wasRunning = endpoint.stop();
+     * }
+ * + * @return {@code true} if the service was running before deregistration, {@code false} otherwise + */ @WriteOperation public boolean stop() { boolean isRunning = isRunning(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java index 2fafbff8..afc898ad 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/endpoint/ServiceRegistrationEndpoint.java @@ -22,6 +22,21 @@ @Endpoint(id = "serviceRegistration") public class ServiceRegistrationEndpoint extends AbstractServiceRegistrationEndpoint { + /** + * Returns metadata about the current service registration, including application name, + * registration details, port, status, and running state. + * This is a read operation exposed via the {@code /actuator/serviceRegistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP GET to /actuator/serviceRegistration
+     * ServiceRegistrationEndpoint endpoint = context.getBean(ServiceRegistrationEndpoint.class);
+     * Map metadata = endpoint.metadata();
+     * String appName = (String) metadata.get("application-name");
+     * }
+ * + * @return a {@link Map} containing service registration metadata + */ @ReadOperation public Map metadata() { Map metadata = new LinkedHashMap<>(16); @@ -38,6 +53,19 @@ public Map metadata() { return metadata; } + /** + * Registers the service with the {@link ServiceRegistry} if it is not already running. + * This is a write operation exposed via the {@code /actuator/serviceRegistration} endpoint. + * + *

Example Usage: + *

{@code
+     * // Via actuator HTTP POST to /actuator/serviceRegistration
+     * ServiceRegistrationEndpoint endpoint = context.getBean(ServiceRegistrationEndpoint.class);
+     * boolean wasAlreadyRunning = endpoint.start();
+     * }
+ * + * @return {@code true} if the service was already running, {@code false} if it was newly registered + */ @WriteOperation public boolean start() { boolean isRunning = isRunning(); From e733ebc13f05f326f3ab72d8325f54f82208ff3b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:04:51 +0000 Subject: [PATCH 5/7] Add JavaDoc with Example Usage sections to non-private methods missing documentation Add JavaDoc comments to constructors, getters, setters, and other non-private methods across 7 files in the registry event and fault tolerance packages. Each JavaDoc follows the project style with brief descriptions, {@link} cross-references, Example Usage sections using
{@code}
blocks, and @param/@return tags where applicable. Files updated: - RegistrationDeregisteredEvent.java - RegistrationPreDeregisteredEvent.java - RegistrationPreRegisteredEvent.java - RegistrationRegisteredEvent.java - WeightedRoundRobin.java - TomcatFaultToleranceAutoConfiguration.java - TomcatDynamicConfigurationListener.java Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../event/RegistrationDeregisteredEvent.java | 27 +++++ .../RegistrationPreDeregisteredEvent.java | 27 +++++ .../event/RegistrationPreRegisteredEvent.java | 27 +++++ .../event/RegistrationRegisteredEvent.java | 27 +++++ .../loadbalancer/WeightedRoundRobin.java | 109 ++++++++++++++++++ ...TomcatFaultToleranceAutoConfiguration.java | 12 ++ .../TomcatDynamicConfigurationListener.java | 73 ++++++++++++ 7 files changed, 302 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java index de176582..45ef54ce 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationDeregisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationDeregisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationDeregisteredEvent} indicating that a + * {@link Registration} has been deregistered from the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationDeregisteredEvent event = new RegistrationDeregisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that performed the deregistration + * @param source the {@link Registration} that was deregistered + */ public RegistrationDeregisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#DEREGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationDeregisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.DEREGISTERED
+     * }
+ * + * @return {@link Type#DEREGISTERED} + */ @Override public Type getType() { return DEREGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java index 3ef61b67..b98fc529 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreDeregisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationPreDeregisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationPreDeregisteredEvent} indicating that a + * {@link Registration} is about to be deregistered from the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationPreDeregisteredEvent event = new RegistrationPreDeregisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that will perform the deregistration + * @param source the {@link Registration} to be deregistered + */ public RegistrationPreDeregisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#PRE_DEREGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationPreDeregisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.PRE_DEREGISTERED
+     * }
+ * + * @return {@link Type#PRE_DEREGISTERED} + */ @Override public Type getType() { return PRE_DEREGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java index 64073258..8ae5c353 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationPreRegisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationPreRegisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationPreRegisteredEvent} indicating that a + * {@link Registration} is about to be registered with the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationPreRegisteredEvent event = new RegistrationPreRegisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that will perform the registration + * @param source the {@link Registration} to be registered + */ public RegistrationPreRegisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#PRE_REGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationPreRegisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.PRE_REGISTERED
+     * }
+ * + * @return {@link Type#PRE_REGISTERED} + */ @Override public Type getType() { return PRE_REGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java index 06306ac8..42932dc7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationRegisteredEvent.java @@ -30,10 +30,37 @@ */ public class RegistrationRegisteredEvent extends RegistrationEvent { + /** + * Create a new {@link RegistrationRegisteredEvent} indicating that a + * {@link Registration} has been registered with the {@link ServiceRegistry}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * RegistrationRegisteredEvent event = new RegistrationRegisteredEvent(registry, registration);
+     * applicationContext.publishEvent(event);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that performed the registration + * @param source the {@link Registration} that was registered + */ public RegistrationRegisteredEvent(ServiceRegistry registry, Registration source) { super(registry, source); } + /** + * Returns the {@link Type} of this event, which is always {@link Type#REGISTERED}. + * + *

Example Usage: + *

{@code
+     * RegistrationRegisteredEvent event = ...;
+     * RegistrationEvent.Type type = event.getType();
+     * // type == RegistrationEvent.Type.REGISTERED
+     * }
+ * + * @return {@link Type#REGISTERED} + */ @Override public Type getType() { return REGISTERED; diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java index dc2f30d8..b2991743 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/loadbalancer/WeightedRoundRobin.java @@ -19,40 +19,149 @@ public class WeightedRoundRobin { private volatile long lastUpdate; + /** + * Create a new {@link WeightedRoundRobin} instance with the given identifier. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * }
+ * + * @param id the unique identifier for this weighted round-robin entry + */ public WeightedRoundRobin(String id) { this.id = id; } + /** + * Get the unique identifier for this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * String id = wrr.getId(); // "server-1"
+     * }
+ * + * @return the identifier + */ public String getId() { return id; } + /** + * Get the current weight of this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * int weight = wrr.getWeight(); // 5
+     * }
+ * + * @return the current weight + */ public int getWeight() { return weight; } + /** + * Set the weight for this {@link WeightedRoundRobin} entry and reset the current counter. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(10);
+     * }
+ * + * @param weight the new weight value + */ public void setWeight(int weight) { this.weight = weight; current.reset(); } + /** + * Increase the current counter by the weight value and return the updated value. + * Used during weighted round-robin selection to accumulate the weight for this entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * long current = wrr.increaseCurrent(); // 5
+     * current = wrr.increaseCurrent();      // 10
+     * }
+ * + * @return the updated current counter value + */ public long increaseCurrent() { current.add(weight); return current.longValue(); } + /** + * Subtract the total weight from the current counter after this entry has been selected. + * This is part of the weighted round-robin algorithm to reduce the selected entry's counter. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * wrr.increaseCurrent();
+     * wrr.sel(10); // subtract total weight of all entries
+     * }
+ * + * @param total the total weight of all entries to subtract + */ public void sel(int total) { current.add(-1 * total); } + /** + * Get the timestamp of the last update to this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setLastUpdate(System.currentTimeMillis());
+     * long lastUpdate = wrr.getLastUpdate();
+     * }
+ * + * @return the last update timestamp in milliseconds + */ public long getLastUpdate() { return lastUpdate; } + /** + * Set the timestamp of the last update to this {@link WeightedRoundRobin} entry. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setLastUpdate(System.currentTimeMillis());
+     * }
+ * + * @param lastUpdate the last update timestamp in milliseconds + */ public void setLastUpdate(long lastUpdate) { this.lastUpdate = lastUpdate; } + /** + * Returns a string representation of this {@link WeightedRoundRobin} including + * its id, weight, current counter, and last update timestamp. + * + *

Example Usage: + *

{@code
+     * WeightedRoundRobin wrr = new WeightedRoundRobin("server-1");
+     * wrr.setWeight(5);
+     * String s = wrr.toString(); // "WeightedRoundRobin[id='server-1', weight=5, current=0, lastUpdate=0]"
+     * }
+ * + * @return a string representation of this entry + */ @Override public String toString() { return new StringJoiner(", ", WeightedRoundRobin.class.getSimpleName() + "[", "]") diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java index 58b1cd92..b32ad811 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/autoconfigure/TomcatFaultToleranceAutoConfiguration.java @@ -72,6 +72,18 @@ public class TomcatFaultToleranceAutoConfiguration { ) public static final String ENABLED_PROPERTY_NAME = TOMCAT_PROPERTY_PREFIX + "." + PropertyConstants.ENABLED_PROPERTY_NAME; + /** + * Handles the {@link WebServerInitializedEvent} to register a {@link TomcatDynamicConfigurationListener} + * when the embedded web server is a {@link TomcatWebServer}. + * + *

Example Usage: + *

{@code
+     * // Automatically invoked by Spring when WebServerInitializedEvent is published.
+     * // The listener is registered as an ApplicationListener on the web application context.
+     * }
+ * + * @param event the {@link WebServerInitializedEvent} triggered after the web server starts + */ @EventListener(WebServerInitializedEvent.class) public void onWebServerInitializedEvent(WebServerInitializedEvent event) { WebServerApplicationContext webServerApplicationContext = event.getApplicationContext(); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java index 10daa3c4..42abdb41 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/fault/tolerance/tomcat/event/TomcatDynamicConfigurationListener.java @@ -68,6 +68,25 @@ public class TomcatDynamicConfigurationListener implements ApplicationListenerExample Usage: + *
{@code
+     * TomcatWebServer tomcatWebServer = ...;
+     * ServerProperties serverProperties = ...;
+     * ConfigurableApplicationContext context = ...;
+     * TomcatDynamicConfigurationListener listener =
+     *     new TomcatDynamicConfigurationListener(tomcatWebServer, serverProperties, context);
+     * context.addApplicationListener(listener);
+     * }
+ * + * @param tomcatWebServer the {@link TomcatWebServer} to reconfigure dynamically + * @param serverProperties the current {@link ServerProperties} + * @param context the {@link ConfigurableApplicationContext} for environment access + */ public TomcatDynamicConfigurationListener(TomcatWebServer tomcatWebServer, ServerProperties serverProperties, ConfigurableApplicationContext context) { this.tomcatWebServer = tomcatWebServer; @@ -85,6 +104,18 @@ private void initCurrentServerProperties() { this.currentServerProperties = getCurrentServerProperties(environment); } + /** + * Handles an {@link EnvironmentChangeEvent} by reconfiguring the Tomcat connector + * if any server-related properties have changed. + * + *

Example Usage: + *

{@code
+     * // Automatically invoked by Spring when an EnvironmentChangeEvent is published.
+     * // Reconfigures Tomcat settings such as thread pool size, connection timeout, etc.
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + */ @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (!isSourceFrom(event)) { @@ -146,6 +177,20 @@ private void configureConnector(ServerProperties refreshableServerProperties) { configureHttp11Protocol(refreshableServerProperties, connector, protocolHandler); } + /** + * Configure the Tomcat {@link AbstractProtocol} settings such as thread pool sizes, + * accept count, connection timeout, and max connections from the refreshed {@link ServerProperties}. + * + *

Example Usage: + *

{@code
+     * ServerProperties refreshedProperties = ...;
+     * ProtocolHandler protocolHandler = connector.getProtocolHandler();
+     * listener.configureProtocol(refreshedProperties, protocolHandler);
+     * }
+ * + * @param refreshableServerProperties the refreshed {@link ServerProperties} to apply + * @param protocolHandler the {@link ProtocolHandler} to configure + */ void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractProtocol) { @@ -194,6 +239,22 @@ void configureProtocol(ServerProperties refreshableServerProperties, ProtocolHan } } + /** + * Configure the Tomcat {@link AbstractHttp11Protocol} settings such as max HTTP header size, + * max swallow size, and max HTTP form POST size from the refreshed {@link ServerProperties}. + * + *

Example Usage: + *

{@code
+     * ServerProperties refreshedProperties = ...;
+     * Connector connector = tomcatWebServer.getTomcat().getConnector();
+     * ProtocolHandler protocolHandler = connector.getProtocolHandler();
+     * listener.configureHttp11Protocol(refreshedProperties, connector, protocolHandler);
+     * }
+ * + * @param refreshableServerProperties the refreshed {@link ServerProperties} to apply + * @param connector the Tomcat {@link Connector} + * @param protocolHandler the {@link ProtocolHandler} to configure + */ void configureHttp11Protocol(ServerProperties refreshableServerProperties, Connector connector, ProtocolHandler protocolHandler) { if (protocolHandler instanceof AbstractHttp11Protocol) { AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) protocolHandler; @@ -236,6 +297,18 @@ private int toIntBytes(DataSize dataSize) { return (int) dataSize.toBytes(); } + /** + * Check whether the given integer value is positive (greater than zero). + * + *

Example Usage: + *

{@code
+     * boolean result = listener.isPositive(10);  // true
+     * boolean result2 = listener.isPositive(0);  // false
+     * }
+ * + * @param value the value to check + * @return {@code true} if the value is greater than zero + */ boolean isPositive(int value) { return value > 0; } From 99f5660ceb7ba5cdac33714532800c5094d66c28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:10:51 +0000 Subject: [PATCH 6/7] Add JavaDoc with Example Usage sections to non-private methods in openfeign module Add comprehensive JavaDoc comments including brief descriptions, @link cross-references, Example Usage sections with
{@code ...}
blocks, and @param/@return tags to all non-private methods that were missing documentation across 14 files in the openfeign module. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ignClientAutoRefreshAutoConfiguration.java | 51 +++++++ ...FeignClientSpecificationPostProcessor.java | 15 ++ .../autorefresh/AutoRefreshCapability.java | 117 +++++++++++++++ ...ignClientConfigurationChangedListener.java | 37 +++++ .../autorefresh/FeignComponentRegistry.java | 127 ++++++++++++++++ .../CompositedRequestInterceptor.java | 99 +++++++++++++ .../components/DecoratedContract.java | 37 +++++ .../components/DecoratedDecoder.java | 41 ++++++ .../components/DecoratedEncoder.java | 39 +++++ .../components/DecoratedErrorDecoder.java | 37 +++++ .../components/DecoratedFeignComponent.java | 138 ++++++++++++++++++ .../components/DecoratedQueryMapEncoder.java | 51 +++++++ .../components/DecoratedRetryer.java | 61 ++++++++ .../components/NoOpRequestInterceptor.java | 21 +++ 14 files changed, 871 insertions(+) diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java index 46f2e3d5..7e851e56 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientAutoRefreshAutoConfiguration.java @@ -23,6 +23,18 @@ @ConditionalOnBean(EnableFeignAutoRefresh.Marker.class) public class FeignClientAutoRefreshAutoConfiguration { + /** + * Creates a {@link FeignBuilderCustomizer} that adds the {@link NoOpRequestInterceptor} + * as a default request interceptor to every Feign client builder. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean; customizes every Feign builder
+     * FeignBuilderCustomizer customizer = addDefaultRequestInterceptorCustomizer();
+     * }
+ * + * @return a {@link FeignBuilderCustomizer} that adds the {@link NoOpRequestInterceptor} + */ @Bean public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() { return builder -> { @@ -30,6 +42,18 @@ public FeignBuilderCustomizer addDefaultRequestInterceptorCustomizer() { }; } + /** + * Handles the {@link ApplicationReadyEvent} to register the + * {@link FeignClientConfigurationChangedListener} after the application is fully initialized. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring event system on application ready
+     * onApplicationReadyEvent(applicationReadyEvent);
+     * }
+ * + * @param event the {@link ApplicationReadyEvent} fired when the application is ready + */ @EventListener(ApplicationReadyEvent.class) public void onApplicationReadyEvent(ApplicationReadyEvent event) { /** @@ -38,11 +62,38 @@ public void onApplicationReadyEvent(ApplicationReadyEvent event) { registerFeignClientConfigurationChangedListener(event); } + /** + * Creates the {@link FeignComponentRegistry} bean that tracks decorated Feign components + * and supports auto-refresh when configuration properties change. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean
+     * FeignComponentRegistry registry = feignClientRegistry(clientProperties, beanFactory);
+     * }
+ * + * @param clientProperties the {@link FeignClientProperties} providing the default config name + * @param beanFactory the {@link BeanFactory} used for component instantiation + * @return a new {@link FeignComponentRegistry} instance + */ @Bean public FeignComponentRegistry feignClientRegistry(FeignClientProperties clientProperties, BeanFactory beanFactory) { return new FeignComponentRegistry(clientProperties.getDefaultConfig(), beanFactory); } + /** + * Creates the {@link FeignClientSpecificationPostProcessor} bean that injects + * the {@link io.microsphere.spring.cloud.openfeign.autorefresh.AutoRefreshCapability} + * into default Feign client specifications. + * + *

Example Usage: + *

{@code
+     * // Automatically registered as a Spring bean
+     * FeignClientSpecificationPostProcessor processor = feignClientSpecificationPostProcessor();
+     * }
+ * + * @return a new {@link FeignClientSpecificationPostProcessor} instance + */ @Bean public FeignClientSpecificationPostProcessor feignClientSpecificationPostProcessor() { return new FeignClientSpecificationPostProcessor(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java index 1dd44c8d..895c3bc2 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autoconfigure/FeignClientSpecificationPostProcessor.java @@ -27,6 +27,21 @@ public class FeignClientSpecificationPostProcessor implements BeanPostProcessor private static final Class FEIGN_CLIENT_SPECIFICATION_CLASS = FeignClientSpecification.class; + /** + * Injects the {@link AutoRefreshCapability} into default {@link FeignClientSpecification} + * beans after initialization. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring container during bean post-processing
+     * Object processed = postProcessAfterInitialization(bean, "default.my-client");
+     * }
+ * + * @param bean the bean instance that has been initialized + * @param beanName the name of the bean in the Spring context + * @return the (possibly modified) bean instance + * @throws BeansException if post-processing fails + */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class beanType = getTargetClass(bean); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java index ebc5b9f2..d9dc7ddd 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/AutoRefreshCapability.java @@ -38,6 +38,19 @@ public class AutoRefreshCapability implements Capability, ApplicationContextAwar private String contextId; + /** + * Constructs an {@link AutoRefreshCapability} with the required dependencies. + * + *

Example Usage: + *

{@code
+     * AutoRefreshCapability capability = new AutoRefreshCapability(
+     *     clientProperties, contextFactory, componentRegistry);
+     * }
+ * + * @param clientProperties the {@link FeignClientProperties} providing Feign client configuration + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param componentRegistry the {@link FeignComponentRegistry} to register decorated components + */ public AutoRefreshCapability(FeignClientProperties clientProperties, NamedContextFactory contextFactory, FeignComponentRegistry componentRegistry) { @@ -46,11 +59,37 @@ public AutoRefreshCapability(FeignClientProperties clientProperties, this.componentRegistry = componentRegistry; } + /** + * Sets the {@link ApplicationContext} and extracts the Feign client context ID + * from the {@code spring.cloud.openfeign.client.name} property. + * + *

Example Usage: + *

{@code
+     * AutoRefreshCapability capability = new AutoRefreshCapability(props, factory, registry);
+     * capability.setApplicationContext(applicationContext);
+     * }
+ * + * @param applicationContext the {@link ApplicationContext} for this Feign client + * @throws BeansException if the context cannot be set + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.contextId = applicationContext.getEnvironment().getProperty("spring.cloud.openfeign.client.name"); } + /** + * Enriches the given {@link Retryer} by wrapping it in a {@link DecoratedRetryer} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Retryer original = new Retryer.Default();
+     * Retryer enriched = capability.enrich(original);
+     * }
+ * + * @param retryer the original {@link Retryer} to enrich, or {@code null} + * @return the decorated {@link Retryer}, or {@code null} if the input is {@code null} + */ @Override public Retryer enrich(Retryer retryer) { if (retryer == null) { @@ -63,6 +102,19 @@ public Retryer enrich(Retryer retryer) { return decoratedRetryer; } + /** + * Enriches the given {@link Contract} by wrapping it in a {@link DecoratedContract} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Contract original = new Contract.Default();
+     * Contract enriched = capability.enrich(original);
+     * }
+ * + * @param contract the original {@link Contract} to enrich, or {@code null} + * @return the decorated {@link Contract}, or {@code null} if the input is {@code null} + */ @Override public Contract enrich(Contract contract) { if (contract == null) { @@ -75,6 +127,19 @@ public Contract enrich(Contract contract) { return decoratedContract; } + /** + * Enriches the given {@link Decoder} by wrapping it in a {@link DecoratedDecoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Decoder original = new Decoder.Default();
+     * Decoder enriched = capability.enrich(original);
+     * }
+ * + * @param decoder the original {@link Decoder} to enrich, or {@code null} + * @return the decorated {@link Decoder}, or {@code null} if the input is {@code null} + */ @Override public Decoder enrich(Decoder decoder) { if (decoder == null) { @@ -87,6 +152,19 @@ public Decoder enrich(Decoder decoder) { return decoratedDecoder; } + /** + * Enriches the given {@link Encoder} by wrapping it in a {@link DecoratedEncoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * Encoder original = new Encoder.Default();
+     * Encoder enriched = capability.enrich(original);
+     * }
+ * + * @param encoder the original {@link Encoder} to enrich, or {@code null} + * @return the decorated {@link Encoder}, or {@code null} if the input is {@code null} + */ @Override public Encoder enrich(Encoder encoder) { if (encoder == null) { @@ -99,6 +177,19 @@ public Encoder enrich(Encoder encoder) { return decoratedEncoder; } + /** + * Enriches the given {@link ErrorDecoder} by wrapping it in a {@link DecoratedErrorDecoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * ErrorDecoder original = new ErrorDecoder.Default();
+     * ErrorDecoder enriched = capability.enrich(original);
+     * }
+ * + * @param decoder the original {@link ErrorDecoder} to enrich, or {@code null} + * @return the decorated {@link ErrorDecoder}, or {@code null} if the input is {@code null} + */ public ErrorDecoder enrich(ErrorDecoder decoder) { if (decoder == null) { return null; @@ -110,6 +201,19 @@ public ErrorDecoder enrich(ErrorDecoder decoder) { return decoratedErrorDecoder; } + /** + * Enriches the given {@link RequestInterceptor} by registering it in the + * {@link FeignComponentRegistry} as part of a {@link io.microsphere.spring.cloud.openfeign.components.CompositedRequestInterceptor}. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor original = template -> template.header("X-Custom", "value");
+     * RequestInterceptor enriched = capability.enrich(original);
+     * }
+ * + * @param requestInterceptor the original {@link RequestInterceptor} to enrich, or {@code null} + * @return the composited {@link RequestInterceptor}, or {@code null} if the input is {@code null} + */ @Override public RequestInterceptor enrich(RequestInterceptor requestInterceptor) { if (requestInterceptor == null) { @@ -118,6 +222,19 @@ public RequestInterceptor enrich(RequestInterceptor requestInterceptor) { return this.componentRegistry.registerRequestInterceptor(contextId, requestInterceptor); } + /** + * Enriches the given {@link QueryMapEncoder} by wrapping it in a {@link DecoratedQueryMapEncoder} + * that supports auto-refresh on configuration changes. + * + *

Example Usage: + *

{@code
+     * QueryMapEncoder original = new QueryMapEncoder.Default();
+     * QueryMapEncoder enriched = capability.enrich(original);
+     * }
+ * + * @param queryMapEncoder the original {@link QueryMapEncoder} to enrich, or {@code null} + * @return the decorated {@link QueryMapEncoder}, or {@code null} if the input is {@code null} + */ @Override public QueryMapEncoder enrich(QueryMapEncoder queryMapEncoder) { if (queryMapEncoder == null) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java index 835245b8..7fae5c63 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignClientConfigurationChangedListener.java @@ -15,12 +15,36 @@ public class FeignClientConfigurationChangedListener implements ApplicationListe private final FeignComponentRegistry registry; + /** + * Constructs a listener that refreshes Feign components when the environment changes. + * + *

Example Usage: + *

{@code
+     * FeignComponentRegistry registry = ...;
+     * FeignClientConfigurationChangedListener listener =
+     *     new FeignClientConfigurationChangedListener(registry);
+     * }
+ * + * @param registry the {@link FeignComponentRegistry} used to refresh affected Feign components + */ public FeignClientConfigurationChangedListener(FeignComponentRegistry registry) { this.registry = registry; } private final String PREFIX = "spring.cloud.openfeign.client.config."; + /** + * Handles an {@link EnvironmentChangeEvent} by resolving which Feign clients are affected + * and triggering a refresh on the corresponding components in the registry. + * + *

Example Usage: + *

{@code
+     * // Invoked automatically by the Spring event system
+     * listener.onApplicationEvent(environmentChangeEvent);
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + */ @Override public void onApplicationEvent(EnvironmentChangeEvent event) { Map> effectiveClients = resolveChangedClient(event); @@ -29,6 +53,19 @@ public void onApplicationEvent(EnvironmentChangeEvent event) { } } + /** + * Resolves which Feign client names and their changed configuration keys are affected + * by the given {@link EnvironmentChangeEvent}. + * + *

Example Usage: + *

{@code
+     * Map> changed = listener.resolveChangedClient(event);
+     * // e.g. {"my-client" -> {"retryer", "decoder"}}
+     * }
+ * + * @param event the {@link EnvironmentChangeEvent} containing the changed property keys + * @return a map of client names to their changed configuration sub-keys + */ protected Map> resolveChangedClient(EnvironmentChangeEvent event) { Set keys = event.getKeys(); return keys.stream() diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java index 45b1d84c..4c20fae0 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/autorefresh/FeignComponentRegistry.java @@ -60,6 +60,18 @@ public class FeignComponentRegistry { private final BeanFactory beanFactory; + /** + * Returns the Feign component class corresponding to the given configuration key. + * + *

Example Usage: + *

{@code
+     * Class componentClass = FeignComponentRegistry.getComponentClass("retryer");
+     * // returns Retryer.class
+     * }
+ * + * @param config the configuration property key (e.g. {@code "retryer"}, {@code "decoder"}) + * @return the mapped Feign component {@link Class}, or {@code null} if not found + */ protected static Class getComponentClass(String config) { if (isBlank(config)) { return null; @@ -68,16 +80,53 @@ protected static Class getComponentClass(String config) { return configComponentMappings.get(normalizedConfig); } + /** + * Normalizes a configuration key by stripping array index suffixes and converting + * to dashed form. + * + *

Example Usage: + *

{@code
+     * String normalized = FeignComponentRegistry.normalizeConfig("requestInterceptors[0]");
+     * // returns "request-interceptors"
+     * }
+ * + * @param config the raw configuration property key + * @return the normalized, dashed-form configuration key + */ static String normalizeConfig(String config) { String normalizedConfig = substringBefore(config, LEFT_SQUARE_BRACKET); return toDashedForm(normalizedConfig); } + /** + * Constructs a {@link FeignComponentRegistry} with the given default client name + * and {@link BeanFactory}. + * + *

Example Usage: + *

{@code
+     * FeignComponentRegistry registry = new FeignComponentRegistry("default", beanFactory);
+     * }
+ * + * @param defaultClientName the name of the default Feign client configuration + * @param beanFactory the {@link BeanFactory} used for component resolution + */ public FeignComponentRegistry(String defaultClientName, BeanFactory beanFactory) { this.defaultClientName = defaultClientName; this.beanFactory = beanFactory; } + /** + * Registers a list of {@link Refreshable} components for the specified Feign client. + * + *

Example Usage: + *

{@code
+     * List components = List.of(decoratedContract, decoratedDecoder);
+     * registry.register("my-client", components);
+     * }
+ * + * @param clientName the Feign client name + * @param components the list of {@link Refreshable} components to register + */ public void register(String clientName, List components) { assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); assertNotEmpty(components, () -> "The 'components' must not be empty!"); @@ -86,10 +135,36 @@ public void register(String clientName, List components) { componentList.addAll(components); } + /** + * Registers a single {@link Refreshable} component for the specified Feign client. + * + *

Example Usage: + *

{@code
+     * registry.register("my-client", decoratedContract);
+     * }
+ * + * @param clientName the Feign client name + * @param component the {@link Refreshable} component to register + */ public void register(String clientName, Refreshable component) { register(clientName, ofList(component)); } + /** + * Registers a {@link RequestInterceptor} for the specified Feign client. Interceptors + * are collected into a {@link CompositedRequestInterceptor} per client. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor interceptor = template -> template.header("X-Custom", "value");
+     * RequestInterceptor result = registry.registerRequestInterceptor("my-client", interceptor);
+     * }
+ * + * @param clientName the Feign client name + * @param requestInterceptor the {@link RequestInterceptor} to register + * @return the {@link CompositedRequestInterceptor} if this is the first interceptor + * for the client, or {@link io.microsphere.spring.cloud.openfeign.components.NoOpRequestInterceptor#INSTANCE} otherwise + */ public RequestInterceptor registerRequestInterceptor(String clientName, RequestInterceptor requestInterceptor) { assertNotBlank(clientName, () -> "The 'clientName' must not be blank!"); assertNotNull(requestInterceptor, () -> "The 'requestInterceptor' must not be null!"); @@ -101,10 +176,35 @@ public RequestInterceptor registerRequestInterceptor(String clientName, RequestI } + /** + * Refreshes the Feign components for the specified client whose configurations have changed. + * + *

Example Usage: + *

{@code
+     * registry.refresh("my-client", "retryer", "decoder");
+     * }
+ * + * @param clientName the Feign client name + * @param changedConfigs the configuration keys that have changed + */ public void refresh(String clientName, String... changedConfigs) { refresh(clientName, ofSet(changedConfigs)); } + /** + * Refreshes the Feign components for the specified client based on a set of changed + * configuration keys. If the default client configuration changed, all registered + * components are refreshed. + * + *

Example Usage: + *

{@code
+     * Set changed = Set.of("my-client.retryer", "my-client.decoder");
+     * registry.refresh("my-client", changed);
+     * }
+ * + * @param clientName the Feign client name + * @param changedConfigs the set of changed configuration sub-keys + */ public synchronized void refresh(String clientName, Set changedConfigs) { Set> effectiveComponents = new HashSet<>(changedConfigs.size()); @@ -144,10 +244,37 @@ public synchronized void refresh(String clientName, Set changedConfigs) } } + /** + * Checks whether the given {@link Refreshable} component's class is assignable from + * any of the effective component classes. + * + *

Example Usage: + *

{@code
+     * boolean present = FeignComponentRegistry.isComponentPresent(
+     *     refreshableComponent, List.of(Retryer.class, Decoder.class));
+     * }
+ * + * @param component the {@link Refreshable} component to check + * @param effectiveComponents the component classes to match against + * @return {@code true} if the component matches any of the effective classes + */ static boolean isComponentPresent(Refreshable component, Iterable> effectiveComponents) { return isComponentClassPresent(component.getClass(), effectiveComponents); } + /** + * Checks whether the given class is assignable from any of the effective component classes. + * + *

Example Usage: + *

{@code
+     * boolean present = FeignComponentRegistry.isComponentClassPresent(
+     *     DecoratedRetryer.class, List.of(Retryer.class));
+     * }
+ * + * @param componentsClass the class to check + * @param effectiveComponents the component classes to match against + * @return {@code true} if the class is assignable from any effective class + */ static boolean isComponentClassPresent(Class componentsClass, Iterable> effectiveComponents) { for (Class actualComponent : effectiveComponents) { if (actualComponent.isAssignableFrom(componentsClass)) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java index 81f65d74..4f051ee3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/CompositedRequestInterceptor.java @@ -33,15 +33,49 @@ public class CompositedRequestInterceptor implements RequestInterceptor, Refresh private final Set set = new LinkedHashSet<>(); + /** + * Constructs a {@link CompositedRequestInterceptor} for the specified Feign client context. + * + *

Example Usage: + *

{@code
+     * CompositedRequestInterceptor interceptor =
+     *     new CompositedRequestInterceptor("my-client", beanFactory);
+     * }
+ * + * @param contextId the Feign client context ID + * @param beanFactory the {@link BeanFactory} for resolving interceptor instances + */ public CompositedRequestInterceptor(String contextId, BeanFactory beanFactory) { this.beanFactory = beanFactory; this.contextId = contextId; } + /** + * Returns an unmodifiable view of the registered {@link RequestInterceptor} instances. + * + *

Example Usage: + *

{@code
+     * Set interceptors = compositedInterceptor.getRequestInterceptors();
+     * }
+ * + * @return an unmodifiable {@link Set} of registered request interceptors + */ public Set getRequestInterceptors() { return unmodifiableSet(set); } + /** + * Applies all registered {@link RequestInterceptor} instances to the given + * {@link RequestTemplate} in order. + * + *

Example Usage: + *

{@code
+     * RequestTemplate template = new RequestTemplate();
+     * compositedInterceptor.apply(template);
+     * }
+ * + * @param template the {@link RequestTemplate} to apply interceptors to + */ @Override public void apply(RequestTemplate template) { synchronized (this.set) { @@ -49,6 +83,19 @@ public void apply(RequestTemplate template) { } } + /** + * Adds a {@link RequestInterceptor} to this composite. Returns {@code true} if this + * is the first interceptor added (i.e., the composite was previously empty). + * + *

Example Usage: + *

{@code
+     * boolean wasFirst = compositedInterceptor.addRequestInterceptor(
+     *     template -> template.header("Authorization", "Bearer token"));
+     * }
+ * + * @param requestInterceptor the {@link RequestInterceptor} to add + * @return {@code true} if this was the first interceptor added, {@code false} otherwise + */ public boolean addRequestInterceptor(RequestInterceptor requestInterceptor) { synchronized (this.set) { boolean isFirst = this.set.isEmpty(); @@ -61,6 +108,16 @@ private RequestInterceptor getInterceptorOrInstantiate(ClassExample Usage: + *
{@code
+     * compositedInterceptor.refresh();
+     * }
+ */ @Override public void refresh() { FeignClientProperties properties = getOrInstantiate(FeignClientProperties.class); @@ -110,6 +167,20 @@ public void refresh() { } } + /** + * Adds all elements from the source collection (obtained via the supplier) into the + * target collection if the source is not empty. + * + *

Example Usage: + *

{@code
+     * Collection target = new ArrayList<>();
+     * addAll(() -> List.of("a", "b"), target);
+     * }
+ * + * @param the element type + * @param sourceSupplier the supplier providing the source collection + * @param target the target collection to add elements to + */ static void addAll(Supplier> sourceSupplier, Collection target) { Collection source = sourceSupplier.get(); if (isNotEmpty(source)) { @@ -117,11 +188,39 @@ static void addAll(Supplier> sourceSupplier, Collection tar } } + /** + * Retrieves a bean of the given type from the {@link BeanFactory}, falling back to + * instantiation via the default constructor if the bean is not available. + * + *

Example Usage: + *

{@code
+     * FeignClientProperties properties = getOrInstantiate(FeignClientProperties.class);
+     * }
+ * + * @param the type of the bean + * @param clazz the class of the bean to retrieve or instantiate + * @return an instance of the requested type + */ T getOrInstantiate(Class clazz) { ObjectProvider beanProvider = this.beanFactory.getBeanProvider(clazz); return beanProvider.getIfAvailable(() -> instantiateClass(clazz)); } + /** + * Puts all entries from the source map (obtained via the supplier) into the target + * map if the source is not empty, without overwriting existing keys. + * + *

Example Usage: + *

{@code
+     * Map target = new HashMap<>();
+     * putIfAbsent(() -> Map.of("key", "value"), target);
+     * }
+ * + * @param the key type + * @param the value type + * @param sourceSupplier the supplier providing the source map + * @param target the target map to add entries to + */ static void putIfAbsent(Supplier> sourceSupplier, Map target) { Map source = sourceSupplier.get(); if (isNotEmpty(source)) { diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java index 490fd88a..38ff43c8 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedContract.java @@ -16,16 +16,53 @@ */ public class DecoratedContract extends DecoratedFeignComponent implements Contract { + /** + * Constructs a {@link DecoratedContract} wrapping the given {@link Contract} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedContract contract = new DecoratedContract(
+     *     "my-client", contextFactory, clientProperties, new Contract.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Contract} to delegate to + */ public DecoratedContract(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Contract delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Contract} class from {@link FeignClientConfiguration}, + * falling back to {@link Contract} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedContract.componentType();
+     * }
+ * + * @return the {@link Contract} component type class + */ @Override protected Class componentType() { Class contractClass = get(FeignClientConfiguration::getContract); return contractClass == null ? Contract.class : contractClass; } + /** + * Parses and validates metadata for the given target type by delegating to the + * underlying {@link Contract}. + * + *

Example Usage: + *

{@code
+     * List metadata = decoratedContract.parseAndValidateMetadata(MyFeignClient.class);
+     * }
+ * + * @param targetType the Feign client interface class to parse + * @return the list of parsed {@link MethodMetadata} + */ @Override public List parseAndValidateMetadata(Class targetType) { return delegate().parseAndValidateMetadata(targetType); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java index 9d3cb79c..21fb832e 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedDecoder.java @@ -19,16 +19,57 @@ */ public class DecoratedDecoder extends DecoratedFeignComponent implements Decoder { + /** + * Constructs a {@link DecoratedDecoder} wrapping the given {@link Decoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedDecoder decoder = new DecoratedDecoder(
+     *     "my-client", contextFactory, clientProperties, new Decoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Decoder} to delegate to + */ public DecoratedDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Decoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Decoder} class from {@link FeignClientConfiguration}, + * falling back to {@link Decoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedDecoder.componentType();
+     * }
+ * + * @return the {@link Decoder} component type class + */ @Override protected Class componentType() { Class decoderClass = get(FeignClientConfiguration::getDecoder); return decoderClass == null ? Decoder.class : decoderClass; } + /** + * Decodes a Feign {@link Response} into an object of the given type by delegating + * to the underlying {@link Decoder}. + * + *

Example Usage: + *

{@code
+     * Object result = decoratedDecoder.decode(response, String.class);
+     * }
+ * + * @param response the Feign {@link Response} to decode + * @param type the target type to decode into + * @return the decoded object + * @throws IOException if an I/O error occurs + * @throws DecodeException if decoding fails + * @throws FeignException if a Feign-specific error occurs + */ @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { return delegate().decode(response, type); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java index f8ae900c..2ede1a4f 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedEncoder.java @@ -17,17 +17,56 @@ */ public class DecoratedEncoder extends DecoratedFeignComponent implements Encoder { + /** + * Constructs a {@link DecoratedEncoder} wrapping the given {@link Encoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedEncoder encoder = new DecoratedEncoder(
+     *     "my-client", contextFactory, clientProperties, new Encoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Encoder} to delegate to + */ public DecoratedEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Encoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Encoder} class from {@link FeignClientConfiguration}, + * falling back to {@link Encoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedEncoder.componentType();
+     * }
+ * + * @return the {@link Encoder} component type class + */ @Override protected Class componentType() { Class encoderClass = get(FeignClientConfiguration::getEncoder); return encoderClass == null ? Encoder.class : encoderClass; } + /** + * Encodes the given object into the {@link RequestTemplate} by delegating to the + * underlying {@link Encoder}. + * + *

Example Usage: + *

{@code
+     * decoratedEncoder.encode(myObject, MyObject.class, requestTemplate);
+     * }
+ * + * @param object the object to encode + * @param bodyType the body type + * @param template the {@link RequestTemplate} to encode into + * @throws EncodeException if encoding fails + */ @Override public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { delegate().encode(object, bodyType, template); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java index 1670573e..7a7c55d3 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedErrorDecoder.java @@ -14,16 +14,53 @@ */ public class DecoratedErrorDecoder extends DecoratedFeignComponent implements ErrorDecoder { + /** + * Constructs a {@link DecoratedErrorDecoder} wrapping the given {@link ErrorDecoder} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedErrorDecoder decoder = new DecoratedErrorDecoder(
+     *     "my-client", contextFactory, clientProperties, new ErrorDecoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link ErrorDecoder} to delegate to + */ public DecoratedErrorDecoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, ErrorDecoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link ErrorDecoder} class from {@link FeignClientConfiguration}, + * falling back to {@link ErrorDecoder.Default} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedErrorDecoder.componentType();
+     * }
+ * + * @return the {@link ErrorDecoder} component type class + */ @Override protected Class componentType() { Class errorDecoderClass = get(FeignClientConfiguration::getErrorDecoder); return errorDecoderClass == null ? Default.class : errorDecoderClass; } + /** + * Decodes an error response by delegating to the underlying {@link ErrorDecoder}. + * + *

Example Usage: + *

{@code
+     * Exception ex = decoratedErrorDecoder.decode("MyClient#myMethod()", response);
+     * }
+ * + * @param methodKey the Feign method key (e.g. {@code "MyClient#myMethod()"}) + * @param response the Feign {@link Response} containing the error + * @return the decoded {@link Exception} + */ @Override public Exception decode(String methodKey, Response response) { return delegate().decode(methodKey, response); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java index c5f4b376..ed1b111d 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedFeignComponent.java @@ -32,6 +32,20 @@ public abstract class DecoratedFeignComponent implements Refreshable { protected volatile T delegate; + /** + * Constructs a {@link DecoratedFeignComponent} wrapping the given delegate. + * + *

Example Usage: + *

{@code
+     * // Typically invoked via a subclass constructor
+     * super(contextId, contextFactory, clientProperties, delegate);
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original component to delegate to + */ public DecoratedFeignComponent(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, T delegate) { this.contextId = contextId; this.contextFactory = contextFactory; @@ -39,6 +53,17 @@ public DecoratedFeignComponent(String contextId, NamedContextFactoryExample Usage: + *
{@code
+     * T component = decoratedFeignComponent.delegate();
+     * }
+ * + * @return the current delegate instance + */ public T delegate() { T delegate = this.delegate; if (delegate == null) { @@ -49,32 +74,110 @@ public T delegate() { return delegate; } + /** + * Loads a component instance of the given type from the {@link NamedContextFactory}, + * falling back to direct instantiation if the bean is not available. + * + *

Example Usage: + *

{@code
+     * Decoder decoder = decoratedFeignComponent.loadInstanceFromContextFactory("my-client", Decoder.class);
+     * }
+ * + * @param the component type + * @param contextId the Feign client context ID + * @param componentType the class of the component to load + * @return the loaded component instance + */ @NonNull public T loadInstanceFromContextFactory(String contextId, Class componentType) { ObjectProvider beanProvider = this.contextFactory.getProvider(contextId, componentType); return beanProvider.getIfAvailable(() -> instantiateClass(componentType)); } + /** + * Returns the Feign client context ID associated with this decorated component. + * + *

Example Usage: + *

{@code
+     * String id = decoratedFeignComponent.contextId();
+     * }
+ * + * @return the context ID string + */ @NonNull public String contextId() { return this.contextId; } + /** + * Refreshes this component by clearing the delegate, causing the next call to + * {@link #delegate()} to reload the instance from the context factory. + * + *

Example Usage: + *

{@code
+     * decoratedFeignComponent.refresh();
+     * }
+ */ public void refresh() { logger.trace("the component[{}] - Refreshing delegate instance[{}] for contextId : '{}'", componentType(), this.delegate, contextId); this.delegate = null; } + /** + * Returns the Feign component type class used to resolve the delegate implementation. + * Subclasses must implement this to return the appropriate configuration class. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedFeignComponent.componentType();
+     * }
+ * + * @return the component type class + */ protected abstract Class componentType(); + /** + * Returns the default {@link FeignClientConfiguration} as defined by the + * {@link FeignClientProperties#getDefaultConfig()} key. + * + *

Example Usage: + *

{@code
+     * FeignClientConfiguration defaultConfig = decoratedFeignComponent.getDefaultConfiguration();
+     * }
+ * + * @return the default {@link FeignClientConfiguration}, or {@code null} if not present + */ public FeignClientConfiguration getDefaultConfiguration() { return this.clientProperties.getConfig().get(this.clientProperties.getDefaultConfig()); } + /** + * Returns the {@link FeignClientConfiguration} for the current Feign client context ID. + * + *

Example Usage: + *

{@code
+     * FeignClientConfiguration currentConfig = decoratedFeignComponent.getCurrentConfiguration();
+     * }
+ * + * @return the current {@link FeignClientConfiguration}, or {@code null} if not present + */ public FeignClientConfiguration getCurrentConfiguration() { return this.clientProperties.getConfig().get(contextId); } + /** + * Retrieves a value from the {@link FeignClientConfiguration} using the provided function, + * checking the default configuration first and then the current context configuration. + * + *

Example Usage: + *

{@code
+     * Class decoderClass = get(FeignClientConfiguration::getDecoder);
+     * }
+ * + * @param the value type + * @param configurationFunction the function to extract a value from the configuration + * @return the extracted value, or {@code null} if not found in either configuration + */ protected T get(Function configurationFunction) { FeignClientConfiguration config = getDefaultConfiguration(); T value = null; @@ -90,27 +193,62 @@ protected T get(Function configurationFunction) return value; } + /** + * Loads the delegate instance from the {@link NamedContextFactory} using the + * component type returned by {@link #componentType()}. + * + *

Example Usage: + *

{@code
+     * T instance = decoratedFeignComponent.loadInstance();
+     * }
+ * + * @return the loaded delegate instance + */ protected T loadInstance() { Class componentType = componentType(); String contextId = contextId(); return loadInstanceFromContextFactory(contextId, componentType); } + /** {@inheritDoc} */ @Override public int hashCode() { return delegate().hashCode(); } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { return delegate().equals(obj); } + /** {@inheritDoc} */ @Override public String toString() { return delegate().toString(); } + /** + * Factory method to instantiate a {@link DecoratedFeignComponent} subclass by locating + * the appropriate constructor via reflection. + * + *

Example Usage: + *

{@code
+     * DecoratedContract contract = DecoratedFeignComponent.instantiate(
+     *     DecoratedContract.class, Contract.class,
+     *     "my-client", contextFactory, clientProperties, originalContract);
+     * }
+ * + * @param the decorated component type + * @param the Feign component type + * @param decoratedClass the {@link DecoratedFeignComponent} subclass to instantiate + * @param componentClass the Feign component interface class + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for context resolution + * @param clientProperties the {@link FeignClientProperties} for configuration + * @param delegate the original delegate instance + * @return a new instance of the decorated component + */ public static , T> W instantiate(Class decoratedClass, Class componentClass, String contextId, diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java index fcfddc41..37eb3d1a 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedQueryMapEncoder.java @@ -25,10 +25,35 @@ public class DecoratedQueryMapEncoder extends DecoratedFeignComponentExample Usage: + *
{@code
+     * DecoratedQueryMapEncoder encoder = new DecoratedQueryMapEncoder(
+     *     "my-client", contextFactory, clientProperties, new QueryMapEncoder.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link QueryMapEncoder} to delegate to + */ public DecoratedQueryMapEncoder(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, QueryMapEncoder delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link QueryMapEncoder} class from {@link FeignClientConfiguration}, + * falling back to {@link PageableSpringQueryMapEncoder} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedQueryMapEncoder.componentType();
+     * }
+ * + * @return the {@link QueryMapEncoder} component type class + */ @Override protected Class componentType() { Class queryMapEncoderClass = getQueryMapEncoder(getCurrentConfiguration()); @@ -42,6 +67,20 @@ private Class getQueryMapEncoder(FeignClientConfiguration feign return getQueryMapEncoder(getQueryMapEncoderMethodHandle, feignClientConfiguration); } + /** + * Retrieves the {@link QueryMapEncoder} class from a {@link FeignClientConfiguration} + * using a {@link MethodHandle} for compatibility across Spring Cloud versions. + * + *

Example Usage: + *

{@code
+     * Class encoderClass = DecoratedQueryMapEncoder.getQueryMapEncoder(
+     *     getQueryMapEncoderMethodHandle, feignClientConfiguration);
+     * }
+ * + * @param methodHandle the {@link MethodHandle} to invoke {@code getQueryMapEncoder} + * @param feignClientConfiguration the configuration to read from + * @return the configured {@link QueryMapEncoder} class, or {@code null} if unavailable + */ static Class getQueryMapEncoder(MethodHandle methodHandle, FeignClientConfiguration feignClientConfiguration) { if (methodHandle == NOT_FOUND_METHOD_HANDLE) { return null; @@ -55,6 +94,18 @@ static Class getQueryMapEncoder(MethodHandle methodHandle, Feig return queryMapEncoderClass; } + /** + * Encodes the given object into a query parameter map by delegating to the + * underlying {@link QueryMapEncoder}. + * + *

Example Usage: + *

{@code
+     * Map queryParams = decoratedQueryMapEncoder.encode(myQueryObject);
+     * }
+ * + * @param object the object to encode as query parameters + * @return a map of query parameter names to values + */ @Override public Map encode(Object object) { return delegate().encode(object); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java index f4dc74fe..ae028a36 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/DecoratedRetryer.java @@ -14,27 +14,88 @@ */ public class DecoratedRetryer extends DecoratedFeignComponent implements Retryer { + /** + * Constructs a {@link DecoratedRetryer} wrapping the given {@link Retryer} delegate. + * + *

Example Usage: + *

{@code
+     * DecoratedRetryer retryer = new DecoratedRetryer(
+     *     "my-client", contextFactory, clientProperties, new Retryer.Default());
+     * }
+ * + * @param contextId the Feign client context ID + * @param contextFactory the {@link NamedContextFactory} for resolving per-client contexts + * @param clientProperties the {@link FeignClientProperties} for configuration lookup + * @param delegate the original {@link Retryer} to delegate to + */ public DecoratedRetryer(String contextId, NamedContextFactory contextFactory, FeignClientProperties clientProperties, Retryer delegate) { super(contextId, contextFactory, clientProperties, delegate); } + /** + * Returns the configured {@link Retryer} class from {@link FeignClientConfiguration}, + * falling back to {@link Retryer} if not configured. + * + *

Example Usage: + *

{@code
+     * Class type = decoratedRetryer.componentType();
+     * }
+ * + * @return the {@link Retryer} component type class + */ @Override protected Class componentType() { Class retryerClass = get(FeignClientConfiguration::getRetryer); return retryerClass == null ? Retryer.class : retryerClass; } + /** + * Continues or propagates the retry by delegating to the underlying {@link Retryer}. + * + *

Example Usage: + *

{@code
+     * try {
+     *     // Feign call
+     * } catch (RetryableException e) {
+     *     decoratedRetryer.continueOrPropagate(e);
+     * }
+     * }
+ * + * @param e the {@link RetryableException} to evaluate for retry + */ @Override public void continueOrPropagate(RetryableException e) { continueOrPropagate(delegate(), e); } + /** + * Delegates the continue-or-propagate decision to the given {@link Retryer} if it + * is not {@code null}. + * + *

Example Usage: + *

{@code
+     * DecoratedRetryer.continueOrPropagate(retryerInstance, retryableException);
+     * }
+ * + * @param retryer the {@link Retryer} to delegate to, may be {@code null} + * @param e the {@link RetryableException} to evaluate + */ static void continueOrPropagate(Retryer retryer, RetryableException e) { if (retryer != null) { retryer.continueOrPropagate(e); } } + /** + * Returns a clone of the delegate {@link Retryer}. + * + *

Example Usage: + *

{@code
+     * Retryer cloned = decoratedRetryer.clone();
+     * }
+ * + * @return a cloned {@link Retryer} instance + */ @Override public Retryer clone() { return delegate().clone(); diff --git a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java index 071c5395..42504bd5 100644 --- a/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java +++ b/microsphere-spring-cloud-openfeign/src/main/java/io/microsphere/spring/cloud/openfeign/components/NoOpRequestInterceptor.java @@ -10,8 +10,29 @@ */ public class NoOpRequestInterceptor implements RequestInterceptor { + /** + * A no-operation {@link RequestInterceptor} that does nothing when applied. + * Used as a placeholder to ensure at least one interceptor is registered. + * + *

Example Usage: + *

{@code
+     * RequestInterceptor noOp = NoOpRequestInterceptor.INSTANCE;
+     * noOp.apply(requestTemplate); // does nothing
+     * }
+ */ public static final NoOpRequestInterceptor INSTANCE = new NoOpRequestInterceptor(); + /** + * Applies this interceptor to the given {@link RequestTemplate}. This implementation + * intentionally performs no operation. + * + *

Example Usage: + *

{@code
+     * NoOpRequestInterceptor.INSTANCE.apply(requestTemplate);
+     * }
+ * + * @param template the {@link RequestTemplate} (ignored) + */ @Override public void apply(RequestTemplate template) { //no op From 6453215cbb8860afdd4c934a7ac240cedd63ef05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:13:56 +0000 Subject: [PATCH 7/7] Add JavaDoc to RegistrationEvent constructor and ServiceInstanceUtils.attachMetadata Co-authored-by: mercyblitz <533114+mercyblitz@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsphere-projects/microsphere-spring-cloud/sessions/30c49647-70b2-4c4a-9853-02725377f52f --- .../registry/event/RegistrationEvent.java | 14 ++++++++++++++ .../service/util/ServiceInstanceUtils.java | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java index 84d9aa3a..939afba7 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/registry/event/RegistrationEvent.java @@ -42,6 +42,20 @@ public abstract class RegistrationEvent extends ApplicationEvent { private final ServiceRegistry registry; + /** + * Create a new {@link RegistrationEvent} with the given {@link ServiceRegistry} and {@link Registration}. + * + *

Example Usage: + *

{@code
+     * ServiceRegistry registry = ...;
+     * Registration registration = ...;
+     * // Typically used through a subclass such as RegistrationRegisteredEvent
+     * RegistrationRegisteredEvent event = new RegistrationRegisteredEvent(registry, registration);
+     * }
+ * + * @param registry the {@link ServiceRegistry} that triggered the event + * @param source the {@link Registration} associated with the event + */ public RegistrationEvent(ServiceRegistry registry, Registration source) { super(source); Assert.notNull(registry, "The 'registry' must not be null"); diff --git a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java index 71aab2bf..f92103fa 100644 --- a/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java +++ b/microsphere-spring-cloud-commons/src/main/java/io/microsphere/spring/cloud/client/service/util/ServiceInstanceUtils.java @@ -65,6 +65,22 @@ public class ServiceInstanceUtils extends BaseUtils { private static final Logger logger = getLogger(ServiceInstanceUtils.class); + /** + * Attach {@link WebEndpointMapping} metadata to the given {@link ServiceInstance}. + * The web endpoint mappings are serialized as JSON and stored in the service instance's + * metadata under the {@link io.microsphere.spring.cloud.client.service.registry.constants.InstanceConstants#WEB_MAPPINGS_METADATA_NAME} key. + * + *

Example Usage: + *

{@code
+     * ServiceInstance serviceInstance = new DefaultServiceInstance("id", "service", "localhost", 8080, false);
+     * Collection mappings = new ArrayList<>();
+     * ServiceInstanceUtils.attachMetadata("/context", serviceInstance, mappings);
+     * }
+ * + * @param contextPath the web application context path + * @param serviceInstance the {@link ServiceInstance} to attach metadata to + * @param webEndpointMappings the collection of {@link WebEndpointMapping}s to attach + */ public static void attachMetadata(String contextPath, ServiceInstance serviceInstance, Collection webEndpointMappings) { Map metadata = serviceInstance.getMetadata(); StringJoiner jsonBuilder = new StringJoiner(COMMA + LINE_SEPARATOR, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET);