Skip to content

Commit

Permalink
refs: eclipse#8 eclipse#9 introduce a way to handle dynamic config ch…
Browse files Browse the repository at this point in the history
…anges

This is just the basic approach for now
  • Loading branch information
struberg committed Nov 22, 2017
1 parent cd1fd2c commit 5026af6
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 1 deletion.
10 changes: 10 additions & 0 deletions api/src/main/java/javax/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
package javax.config;

import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;

import javax.config.spi.ConfigSource;

Expand Down Expand Up @@ -127,4 +129,12 @@ public interface Config {
* @return all currently registered {@link ConfigSource configsources} sorted with descending ordinal and ConfigSource name
*/
Iterable<ConfigSource> getConfigSources();

/**
* A user can register a lambda which gets notified when any configured value
* got changed. The parameter are the config key names which did change.
*
* @param configChangedListener
*/
void registerConfigChangedListener(Consumer<Set<String>> configChangedListener);
}
15 changes: 14 additions & 1 deletion api/src/main/java/javax/config/spi/ConfigSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

/**
* <p>Implement this interfaces to provide a ConfigSource.
Expand Down Expand Up @@ -64,6 +65,8 @@
*/
public interface ConfigSource {
String CONFIG_ORDINAL = "config_ordinal";
int DEFAULT_ORDINAL = 100;

/**
* Return the properties in this config source
* @return the map containing the properties in this config source
Expand Down Expand Up @@ -114,7 +117,7 @@ default int getOrdinal() {

}
}
return 100;
return DEFAULT_ORDINAL;
}

/**
Expand All @@ -131,4 +134,14 @@ default int getOrdinal() {
*/
String getName();

/**
* This callback should get invoked if an attribute change got detected.
*
* @param reportAttributeChange will be set by the {@link ConfigSourceProvider} after this
* {@code ConfigSource} got created and before any configured values
* get served.
*/
default void setOnAttributeChange(Consumer<Set<String>> reportAttributeChange) {
// do nothing by default. Just for compat with older ConfigSources.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2016-2017 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* Licensed 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.eclipse.configjsr;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.config.Config;
import javax.config.spi.ConfigSource;
import javax.inject.Inject;

import org.eclipse.configjsr.dynamic.DynamicChangeConfigSource;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

/**
* @author <a href="mailto:[email protected]">Mark Struberg</a>
*/
public class DynamicConfigSourceTest extends Arquillian {

private @Inject Config config;

@Deployment
public static WebArchive deploy() {
JavaArchive testJar = ShrinkWrap
.create(JavaArchive.class, "dynamicValuesTest.jar")
.addClass(DynamicConfigSourceTest.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsServiceProvider(ConfigSource.class, DynamicChangeConfigSource.class)
.as(JavaArchive.class);


WebArchive war = ShrinkWrap
.create(WebArchive.class, "dynamicValuesTest.war")
.addAsLibrary(testJar);
return war;
}


@Test
public void testBgCount() throws Exception {
Integer value = config.getValue(DynamicChangeConfigSource.TEST_ATTRIBUTE, Integer.class);
Thread.sleep(20L);
Integer value2 = config.getValue(DynamicChangeConfigSource.TEST_ATTRIBUTE, Integer.class);
assertTrue(value2 > value);
}

@Test
public void testBgCallback() throws Exception {
Integer value = config.getValue(DynamicChangeConfigSource.TEST_ATTRIBUTE, Integer.class);
Map<String, Integer> vals = new ConcurrentHashMap<>();


config.registerConfigChangedListener(s -> s.forEach(k -> vals.put(k, config.getValue(k, Integer.class))));
vals.clear();

Thread.sleep(12L);
assertEquals(1, vals.size());

int i1 = vals.get(DynamicChangeConfigSource.TEST_ATTRIBUTE);

Thread.sleep(15L);
assertEquals(1, vals.size());

int i2 = vals.get(DynamicChangeConfigSource.TEST_ATTRIBUTE);
assertTrue(i2 > i1);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.eclipse.configjsr.dynamic;

import java.io.Closeable;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import javax.config.spi.ConfigSource;

/**
* @author <a href="mailto:[email protected]">Mark Struberg</a>
*/
public class DynamicChangeConfigSource implements ConfigSource, Closeable {
public static final String TEST_ATTRIBUTE = "tck.config.test.javaconfig.dynymic.testattrib";

private Consumer<Set<String>> reportAttributeChange;
private AtomicInteger i = new AtomicInteger(0);
private Map<String, String> properties = new HashMap<>();
private Thread worker;

public DynamicChangeConfigSource() {
// start a new backgroundthread.
worker = new Thread() {
@Override
public void run() {
while (i.incrementAndGet() < 10_000) {
reportAttributeChange.accept(Collections.singleton(TEST_ATTRIBUTE));
try {
Thread.sleep(10L);
}
catch (InterruptedException e) {
return;
}
}
}
};
worker.start();
}

@Override
public void close() throws IOException {
i.set(10_001);
}

@Override
public Map<String, String> getProperties() {
properties.put(TEST_ATTRIBUTE, Integer.toString(i.get()));
return properties;
}

@Override
public String getValue(String propertyName) {
return getProperties().get(propertyName);
}

@Override
public String getName() {
return this.getClass().getSimpleName();
}

@Override
public void setOnAttributeChange(Consumer<Set<String>> reportAttributeChange) {
this.reportAttributeChange = reportAttributeChange;
}

}

0 comments on commit 5026af6

Please sign in to comment.