Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update ServiceProviderPlace to support config refresh #1035

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/main/java/emissary/directory/DirectoryPlace.java
Original file line number Diff line number Diff line change
Expand Up @@ -1186,10 +1186,6 @@ public int removePlaces(final List<String> keys) {
*/
@Override
public int irdRemovePlaces(@Nullable final List<String> keys, final boolean propagating) {
if (this.emissaryNode.isStandalone()) {
logger.debug("Cannot remove remote places in standalone nodes");
return 0;
}

if ((keys == null) || keys.isEmpty()) {
logger.warn("Ignoring null or empty key list for irdRemovePlaces");
Expand Down
73 changes: 71 additions & 2 deletions src/main/java/emissary/place/ServiceProviderPlace.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import emissary.util.JMXUtil;

import com.codahale.metrics.Timer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -110,6 +111,9 @@ public abstract class ServiceProviderPlace implements IServiceProviderPlace,
@Nullable
protected String serviceDescription;

private String placeLocation;
private final List<String> configLocs = new ArrayList<>();

/**
* Static context logger
*/
Expand Down Expand Up @@ -253,12 +257,11 @@ protected Configurator loadConfigurator(@Nullable String placeLocation) throws I
if (placeLocation == null) {
placeLocation = this.getClass().getSimpleName();
}

this.placeLocation = placeLocation;
// Extract config data stream name from place location
// and try finding config info with and without the
// package name of this class (in that order)
String myPackage = this.getClass().getPackage().getName();
List<String> configLocs = new ArrayList<>();
// Dont use KeyManipulator for this, only works when hostname/fqdn has dots
int pos = placeLocation.lastIndexOf("/");
String serviceClass = (pos > -1 ? placeLocation.substring(pos + 1) : placeLocation);
Expand Down Expand Up @@ -925,6 +928,72 @@ protected void deregisterFromDirectory(List<String> keys) {
}
}

/**
* Reset the keys and reload the {@link Configurator}
*
* @throws IOException if there is an issue loading the config
*/
protected void refresh() throws IOException {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It occurs to me that this might not be safe to call while the place is actively processing an IBDO, or while trying to identify the next place that should process a payload.

clearKeys();
this.configG = refreshConfigurator();
setupPlace(this.dirPlace, this.placeLocation);
}

/**
* Reset the keys and reload the {@link Configurator}
*
* @param configStream the configuration input stream to load
* @throws IOException if there is an issue loading the config
*/
protected void refresh(@Nullable final InputStream configStream) throws IOException {
clearKeys();
this.configG = refreshConfigurator(configStream);
setupPlace(this.dirPlace, this.placeLocation);
}

/**
* Reload the {@link Configurator}
*
* @throws IOException if there is an issue loading the config
*/
protected Configurator refreshConfigurator() throws IOException {
return refreshConfigurator(this.configLocs);
}

/**
* Reload the {@link Configurator}
*
* @param configLocations the list of configuration files to load
* @throws IOException if there is an issue loading the config
*/
protected Configurator refreshConfigurator(@Nullable final List<String> configLocations) throws IOException {
if (CollectionUtils.isNotEmpty(configLocations)) {
return ConfigUtil.getConfigInfo(configLocations);
}
throw new IOException("No config locations specified");
}

/**
* Reload the {@link Configurator}
*
* @param configStream the stream of configuration data
* @throws IOException if there is an issue loading the config
*/
protected Configurator refreshConfigurator(@Nullable final InputStream configStream) throws IOException {
if (configStream != null) {
return ConfigUtil.getConfigInfo(configStream);
}
throw new IOException("Null config stream supplied");
}

/**
* Unbind from the namespace and clear all loaded keys
*/
protected void clearKeys() {
unbindFromNamespace();
new ArrayList<>(this.keys).forEach(this::removeKey);
this.keys.clear();
}

/**
* Remove a service proxy from the running place. Proxy strings not found registered will be ignored Will remove all
Expand Down
49 changes: 49 additions & 0 deletions src/test/java/emissary/place/ServiceProviderPlaceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import emissary.core.EmissaryException;
import emissary.core.IBaseDataObject;
import emissary.core.Namespace;
import emissary.core.NamespaceException;
import emissary.directory.DirectoryEntry;
import emissary.directory.KeyManipulator;
import emissary.test.core.junit5.UnitTest;
Expand All @@ -24,6 +25,7 @@
import java.util.Set;
import javax.annotation.Nullable;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
Expand Down Expand Up @@ -552,6 +554,53 @@ void testDeniedServiceProxy() {
}
}

private static final byte[] cfgData = ("SERVICE_KEY = \"UNKNOWN.TEST_PLACE.ID.http://localhost:8001/PlaceTest$6050\"\n" +
"KEY_1 = 200").getBytes();
private static final byte[] cfgDataReload = ("SERVICE_KEY = \"*.TEST_PLACE.ID.http://localhost:8001/PlaceTest$5060\"\n" +
"KEY_1 = 300").getBytes();

@Test
void refreshConfigurator() throws IOException {
PlaceTest placeTest = new PlaceTest(new ByteArrayInputStream(cfgData));
assertNotNull(placeTest, "Place created and configured");
assertEquals("PlaceTest", placeTest.getPlaceName(), "Configured place name");
assertEquals("UNKNOWN", placeTest.getPrimaryProxy(), "Primary proxy");
assertEquals("UNKNOWN.TEST_PLACE.ID.http://localhost:8001/PlaceTest", placeTest.getKey(), "Key generation");
DirectoryEntry de = placeTest.getDirectoryEntry();
assertNotNull(de, "Directory entry");
assertEquals(60, de.getCost(), "Cost in directory entry");
assertEquals(50, de.getQuality(), "Quality in directory entry");
assertEquals("Description not available", de.getDescription(), "Description in directory entry");
assertNotNull(placeTest.configG);
assertEquals(200, placeTest.configG.findIntEntry("KEY_1", 0));
assertDoesNotThrow(() -> Namespace.lookup("http://localhost:8001/PlaceTest"));

placeTest.refresh(new ByteArrayInputStream(cfgDataReload));
assertNotNull(placeTest, "Place created and configured");
assertEquals("PlaceTest", placeTest.getPlaceName(), "Configured place name");
assertEquals("*", placeTest.getPrimaryProxy(), "Primary proxy");
assertEquals("*.TEST_PLACE.ID.http://localhost:8001/PlaceTest", placeTest.getKey(), "Key generation");
de = placeTest.getDirectoryEntry();
assertNotNull(de, "Directory entry");
assertEquals(50, de.getCost(), "Cost in directory entry");
assertEquals(40, de.getQuality(), "Quality in directory entry");
assertEquals("Description not available", de.getDescription(), "Description in directory entry");
assertNotNull(placeTest.configG);
assertEquals(300, placeTest.configG.findIntEntry("KEY_1", 0));
assertDoesNotThrow(() -> Namespace.lookup("http://localhost:8001/PlaceTest"));
}

@Test
void testClearKeys() throws IOException {
PlaceTest placeTest = new PlaceTest(new ByteArrayInputStream(cfgData));
assertFalse(placeTest.keys.isEmpty());
assertDoesNotThrow(() -> Namespace.lookup("http://localhost:8001/PlaceTest"));

placeTest.clearKeys();
assertTrue(placeTest.keys.isEmpty());
assertThrows(NamespaceException.class, () -> Namespace.lookup("http://localhost:8001/PlaceTest"));
}

private static final class PlaceTest extends ServiceProviderPlace {

public PlaceTest() throws IOException {
Expand Down
Loading