-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added abstract RefreshableServiceProviderPlace
- Loading branch information
Showing
3 changed files
with
262 additions
and
3 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
src/main/java/emissary/place/RefreshableServiceProviderPlace.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package emissary.place; | ||
|
||
import emissary.config.ConfigUtil; | ||
import emissary.config.Configurator; | ||
|
||
import org.apache.commons.collections4.CollectionUtils; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.List; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* ServiceProviderPlace that supports on-demand refresh of its configuration | ||
*/ | ||
public abstract class RefreshableServiceProviderPlace extends ServiceProviderPlace { | ||
|
||
private final AtomicBoolean invalidated = new AtomicBoolean(false); | ||
|
||
public RefreshableServiceProviderPlace() throws IOException {} | ||
|
||
public RefreshableServiceProviderPlace(final String thePlaceLocation) throws IOException { | ||
super(thePlaceLocation); | ||
} | ||
|
||
protected RefreshableServiceProviderPlace(final String configFile, @Nullable final String theDir, final String thePlaceLocation) | ||
throws IOException { | ||
super(configFile, theDir, thePlaceLocation); | ||
} | ||
|
||
protected RefreshableServiceProviderPlace(final InputStream configStream, @Nullable final String theDir, final String thePlaceLocation) | ||
throws IOException { | ||
super(configStream, theDir, thePlaceLocation); | ||
} | ||
|
||
protected RefreshableServiceProviderPlace(final InputStream configStream) throws IOException { | ||
super(configStream); | ||
} | ||
|
||
protected RefreshableServiceProviderPlace(final String configFile, final String placeLocation) throws IOException { | ||
super(configFile, placeLocation); | ||
} | ||
|
||
protected RefreshableServiceProviderPlace(final InputStream configStream, final String placeLocation) throws IOException { | ||
super(configStream, placeLocation); | ||
} | ||
|
||
/** | ||
* Get the invalid flag of the place. An invalidated place may indicate that the place has changes, such as new | ||
* configuration, and may trigger a follow-on process to reconfigure, reinitialize, or re-create the place. | ||
* | ||
* @return true if the place has been invalidated, false otherwise | ||
*/ | ||
public final boolean isInvalidated() { | ||
return this.invalidated.get(); | ||
} | ||
|
||
/** | ||
* Invalidate a place that need to be refreshed. | ||
*/ | ||
public final void invalidate() { | ||
setInvalidated(true); | ||
} | ||
|
||
/** | ||
* Set the invalid flag of the place | ||
* | ||
* @param invalid true if place is invalid, false otherwise | ||
*/ | ||
private void setInvalidated(final boolean invalid) { | ||
this.invalidated.set(invalid); | ||
} | ||
|
||
/** | ||
* Reinitialize the place by reloading the configurator and reconfiguring the place. Must call {@link #invalidate()} | ||
* before attempting to refresh the place. | ||
*/ | ||
public final synchronized void refresh() { | ||
try { | ||
if (isInvalidated()) { | ||
this.configG = reloadConfigurator(this.configLocs); | ||
reconfigurePlace(); | ||
setInvalidated(false); | ||
} else { | ||
logger.warn("Cannot refresh place configuration without first calling invalidate; no reconfiguration performed"); | ||
} | ||
} catch (IOException e) { | ||
logger.error("Failed to reload configurator"); | ||
} | ||
} | ||
|
||
/** | ||
* Reinitialize the place by reloading the configurator and reconfiguring the place. Must call {@link #invalidate()} | ||
* before attempting to refresh the place. | ||
* | ||
* @param configStream the config data as an {@link InputStream} | ||
*/ | ||
public final synchronized void refresh(final InputStream configStream) { | ||
try { | ||
if (isInvalidated()) { | ||
this.configG = reloadConfigurator(configStream); | ||
reconfigurePlace(); | ||
setInvalidated(false); | ||
} else { | ||
logger.warn("Cannot refresh place configuration with configStream without first calling invalidate; no reconfiguration performed"); | ||
} | ||
} catch (IOException e) { | ||
logger.error("Failed to reload configStream"); | ||
} | ||
} | ||
|
||
protected abstract void reconfigurePlace() throws IOException; | ||
|
||
/** | ||
* Reload the {@link Configurator} | ||
* | ||
* @param configLocations the list of configuration files to load | ||
* @throws IOException if there is an issue loading the config | ||
*/ | ||
private static Configurator reloadConfigurator(@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 | ||
*/ | ||
private static Configurator reloadConfigurator(@Nullable final InputStream configStream) throws IOException { | ||
if (configStream != null) { | ||
return ConfigUtil.getConfigInfo(configStream); | ||
} | ||
throw new IOException("Null config stream supplied"); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/test/java/emissary/place/RefreshableServiceProviderPlaceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package emissary.place; | ||
|
||
import emissary.core.IBaseDataObject; | ||
import emissary.core.Namespace; | ||
import emissary.directory.DirectoryEntry; | ||
import emissary.test.core.junit5.UnitTest; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
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.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class RefreshableServiceProviderPlaceTest extends UnitTest { | ||
|
||
private static final byte[] cfgData = ("SERVICE_KEY = \"UNKNOWN.TEST_PLACE.ID.http://localhost:8001/RefreshablePlaceTest$6050\"\n" + | ||
"KEY_1 = 200").getBytes(); | ||
private static final byte[] cfgDataReload = ("SERVICE_KEY = \"*.TEST_PLACE.ID.http://localhost:8001/RefreshablePlaceTest$5060\"\n" + | ||
"KEY_1 = 300").getBytes(); | ||
|
||
@Nullable | ||
private RefreshablePlaceTest place = null; | ||
|
||
@Override | ||
@BeforeEach | ||
public void setUp() throws Exception { | ||
InputStream config = new ByteArrayInputStream(cfgData); | ||
place = new RefreshablePlaceTest(config, null, "http://localhost:8001/RefreshablePlaceTest"); | ||
} | ||
|
||
@Override | ||
@AfterEach | ||
public void tearDown() throws Exception { | ||
super.tearDown(); | ||
assertNotNull(place); | ||
place.shutDown(); | ||
place = null; | ||
} | ||
|
||
@Test | ||
void testReconfigure() { | ||
assertNotNull(place, "Place created and configured"); | ||
assertEquals("RefreshablePlaceTest", place.getPlaceName(), "Configured place name"); | ||
assertEquals("UNKNOWN", place.getPrimaryProxy(), "Primary proxy"); | ||
assertEquals("UNKNOWN.TEST_PLACE.ID.http://localhost:8001/RefreshablePlaceTest", place.getKey(), "Key generation"); | ||
DirectoryEntry de = place.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(place.configG); | ||
assertEquals(200, place.configG.findIntEntry("KEY_1", 0)); | ||
assertDoesNotThrow(() -> Namespace.lookup("http://localhost:8001/RefreshablePlaceTest")); | ||
|
||
place.invalidate(); | ||
place.refresh(new ByteArrayInputStream(cfgDataReload)); | ||
assertNotNull(place, "Place created and configured"); | ||
assertEquals("RefreshablePlaceTest", place.getPlaceName(), "Configured place name"); | ||
// assertEquals("*", placeTest.getPrimaryProxy(), "Primary proxy"); | ||
assertEquals("UNKNOWN", place.getPrimaryProxy(), "Primary proxy"); | ||
// assertEquals("*.TEST_PLACE.ID.http://localhost:8001/PlaceTest", placeTest.getKey(), "Key generation"); | ||
assertEquals("UNKNOWN.TEST_PLACE.ID.http://localhost:8001/RefreshablePlaceTest", place.getKey(), "Key generation"); | ||
de = place.getDirectoryEntry(); | ||
assertNotNull(de, "Directory entry"); | ||
// assertEquals(50, de.getCost(), "Cost in directory entry"); | ||
assertEquals(60, de.getCost(), "Cost in directory entry"); | ||
// assertEquals(40, de.getQuality(), "Quality in directory entry"); | ||
assertEquals(50, de.getQuality(), "Quality in directory entry"); | ||
assertEquals("Description not available", de.getDescription(), "Description in directory entry"); | ||
assertNotNull(place.configG); | ||
assertEquals(300, place.configG.findIntEntry("KEY_1", 0)); | ||
assertDoesNotThrow(() -> Namespace.lookup("http://localhost:8001/RefreshablePlaceTest")); | ||
} | ||
|
||
@Test | ||
void testInvalidate() { | ||
assertNotNull(place, "Place created and configured"); | ||
assertFalse(place.isInvalidated()); | ||
assertNotNull(place.configG); | ||
assertEquals(200, place.configG.findIntEntry("KEY_1", 0)); | ||
|
||
place.refresh(new ByteArrayInputStream(cfgDataReload)); | ||
assertFalse(place.isInvalidated()); | ||
assertEquals(200, place.configG.findIntEntry("KEY_1", 0)); | ||
|
||
place.invalidate(); | ||
assertTrue(place.isInvalidated()); | ||
place.refresh(new ByteArrayInputStream(cfgDataReload)); | ||
assertFalse(place.isInvalidated()); | ||
assertEquals(300, place.configG.findIntEntry("KEY_1", 0)); | ||
} | ||
|
||
private static final class RefreshablePlaceTest extends RefreshableServiceProviderPlace { | ||
|
||
public RefreshablePlaceTest(InputStream config, @Nullable String dir, @Nullable String loc) throws IOException { | ||
super(config, dir, loc); | ||
} | ||
|
||
@Override | ||
public void process(IBaseDataObject d) { | ||
assertNotNull(d); | ||
} | ||
|
||
@Override | ||
protected void reconfigurePlace() {} | ||
} | ||
} |