This is an automated email from the ASF dual-hosted git repository.
stefanegli pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-discovery-standalone.git
The following commit(s) were added to refs/heads/master by this push:
new 38ab13b SLING-12870 -Introduce offline mode to discovery-standalone
(#1)
38ab13b is described below
commit 38ab13bd72de1dd48eb41308e871ac21e1a6d90b
Author: ionutzpi <[email protected]>
AuthorDate: Wed Aug 6 14:03:58 2025 +0300
SLING-12870 -Introduce offline mode to discovery-standalone (#1)
---
.../sling/discovery/impl/standalone/Config.java | 38 +++++++++++++
.../impl/standalone/NoClusterDiscoveryService.java | 27 ++++++++-
.../standalone/NoClusterDiscoveryServiceTest.java | 64 +++++++++++++++++++++-
3 files changed, 125 insertions(+), 4 deletions(-)
diff --git
a/src/main/java/org/apache/sling/discovery/impl/standalone/Config.java
b/src/main/java/org/apache/sling/discovery/impl/standalone/Config.java
new file mode 100644
index 0000000..2cafdac
--- /dev/null
+++ b/src/main/java/org/apache/sling/discovery/impl/standalone/Config.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.discovery.impl.standalone;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+/**
+ * Configuration interface for the standalone discovery service.
+ */
+@ObjectClassDefinition(
+ name = "Apache Sling Discovery Standalone Configuration",
+ description = "Configuration for the standalone discovery service
implementation"
+)
+public @interface Config {
+
+ @AttributeDefinition(
+ name = "Always Offline",
+ description = "When enabled, it will never report a valid topology and
no TOPOLOGY_INIT events will be sent to TopologyEventListeners."
+ )
+ boolean always_offline() default false;
+}
diff --git
a/src/main/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryService.java
b/src/main/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryService.java
index a028a00..ef9ac19 100644
---
a/src/main/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryService.java
+++
b/src/main/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryService.java
@@ -36,6 +36,7 @@ import org.apache.sling.settings.SlingSettingsService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
@@ -47,6 +48,7 @@ import org.slf4j.LoggerFactory;
* which can be used for a cluster less installation (= single instance).
*/
@Component(immediate=true, service = {DiscoveryService.class}) // immediate as
this is component is also handling the listeners
+@Designate(ocd = Config.class)
public class NoClusterDiscoveryService implements DiscoveryService {
/** The logger. */
@@ -57,6 +59,11 @@ public class NoClusterDiscoveryService implements
DiscoveryService {
*/
private final SlingSettingsService settingsService;
+ /**
+ * Configuration flag to always report as offline.
+ */
+ private volatile boolean alwaysOffline = false;
+
/**
* All topology event listeners.
*/
@@ -84,9 +91,13 @@ public class NoClusterDiscoveryService implements
DiscoveryService {
* Create a new description.
*/
@Activate
- public NoClusterDiscoveryService(@Reference final SlingSettingsService
slingSettingsService) {
+ public NoClusterDiscoveryService(@Reference final SlingSettingsService
slingSettingsService, final Config config) {
logger.debug("NoClusterDiscoveryService started.");
this.settingsService = slingSettingsService;
+ this.alwaysOffline = config.always_offline();
+ if (this.alwaysOffline) {
+ logger.info("Discovery service configured to always report as
offline");
+ }
this.createNewView(Type.TOPOLOGY_INIT, false);
}
@@ -118,10 +129,17 @@ public class NoClusterDiscoveryService implements
DiscoveryService {
final InstanceDescription myInstanceDescription = new
InstanceDescriptionImpl(this.settingsService.getSlingId(),
this.cachedProperties);
this.currentTopologyView = new
TopologyViewImpl(myInstanceDescription);
+
+ // If always offline is enabled, mark the topology as not current
+ if (this.alwaysOffline) {
+ this.currentTopologyView.invalidate();
+ }
+
registeredServices = this.listeners;
newView = this.currentTopologyView;
- if ( inform ) {
+ // Only inform listeners if not in always offline mode
+ if ( inform && !this.alwaysOffline ) {
for(final TopologyEventListener da: registeredServices) {
da.handleTopologyEvent(new TopologyEvent(eventType,
oldView, newView));
}
@@ -205,7 +223,10 @@ public class NoClusterDiscoveryService implements
DiscoveryService {
currentList.add(listener);
this.listeners = currentList.toArray(new
TopologyEventListener[currentList.size()]);
}
- listener.handleTopologyEvent(new TopologyEvent(Type.TOPOLOGY_INIT,
null, this.currentTopologyView));
+ // Only send TOPOLOGY_INIT event if not in always offline mode
+ if (!this.alwaysOffline) {
+ listener.handleTopologyEvent(new TopologyEvent(Type.TOPOLOGY_INIT,
null, this.currentTopologyView));
+ }
}
@SuppressWarnings("unused")
diff --git
a/src/test/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryServiceTest.java
b/src/test/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryServiceTest.java
index 8c54e91..404032a 100644
---
a/src/test/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryServiceTest.java
+++
b/src/test/java/org/apache/sling/discovery/impl/standalone/NoClusterDiscoveryServiceTest.java
@@ -58,6 +58,10 @@ public class NoClusterDiscoveryServiceTest {
}
private DiscoveryService createService() {
+ return createService(false);
+ }
+
+ private DiscoveryService createService(boolean alwaysOffline) {
final DiscoveryService service = new NoClusterDiscoveryService(new
SlingSettingsService() {
@Override
@@ -84,11 +88,25 @@ public class NoClusterDiscoveryServiceTest {
public String getAbsolutePathWithinSlingHome(String relativePath) {
return null;
}
- });
+ }, createMockConfig(alwaysOffline));
return service;
}
+ private Config createMockConfig(final boolean alwaysOffline) {
+ return new Config() {
+ @Override
+ public boolean always_offline() {
+ return alwaysOffline;
+ }
+
+ @Override
+ public Class<? extends java.lang.annotation.Annotation>
annotationType() {
+ return Config.class;
+ }
+ };
+ }
+
@Test public void testBasics() throws Exception {
final DiscoveryService service = this.createService();
@@ -100,6 +118,33 @@ public class NoClusterDiscoveryServiceTest {
assertNull(service.getTopology());
}
+ @Test public void testAlwaysOffline() throws Exception {
+ final DiscoveryService service = this.createService(true);
+
+ assertNotNull(service.getTopology());
+ // When always offline is true, the topology should not be current
+ assertFalse(service.getTopology().isCurrent());
+ // The instance should still be a leader (as per Discovery API)
+ assertTrue(service.getTopology().getLocalInstance().isLeader());
+
+ invoke(service, "deactivate");
+
+ assertNull(service.getTopology());
+ }
+
+ @Test public void testAlwaysOnline() throws Exception {
+ final DiscoveryService service = this.createService(false);
+
+ assertNotNull(service.getTopology());
+ assertTrue(service.getTopology().isCurrent());
+ // When always offline is false, the instance should be a leader
+ assertTrue(service.getTopology().getLocalInstance().isLeader());
+
+ invoke(service, "deactivate");
+
+ assertNull(service.getTopology());
+ }
+
@Test public void testListener() throws Exception {
final DiscoveryService service = this.createService();
@@ -119,6 +164,23 @@ public class NoClusterDiscoveryServiceTest {
assertNull(events.get(0).getOldView());
}
+ @Test public void testListenerAlwaysOffline() throws Exception {
+ final DiscoveryService service = this.createService(true);
+
+ final List<TopologyEvent> events = new ArrayList<TopologyEvent>();
+
+ final TopologyEventListener listener = new TopologyEventListener() {
+
+ @Override
+ public void handleTopologyEvent(final TopologyEvent event) {
+ events.add(event);
+ }
+ };
+ invoke(service, "bindTopologyEventListener", new Class[]
{TopologyEventListener.class}, new Object[] {listener});
+ // When always offline is true, no events should be sent
+ assertEquals(0, events.size());
+ }
+
@Test public void testPropertyChanges() throws Exception {
final DiscoveryService service = this.createService();