blob: 304d40db0bf38be3d24fae7a88825ab87625da73 [file] [log] [blame]
// ***************************************************************************************************************************
// * 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.juneau.config;
import static org.apache.juneau.testutils.TestUtils.*;
import static org.junit.Assert.*;
import java.util.*;
import java.util.concurrent.*;
import org.apache.juneau.*;
import org.apache.juneau.config.event.*;
import org.apache.juneau.config.internal.*;
import org.apache.juneau.config.store.*;
import org.junit.*;
public class ConfigMapListenerTest {
//-----------------------------------------------------------------------------------------------------------------
// Sanity tests.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testBasicDefaultSection() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"foo=bar"
);
final CountDownLatch latch = new CountDownLatch(1);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(foo = baz)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("", "foo", "baz", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("foo = baz|", cm.toString());
}
@Test
public void testBasicNormalSection() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"[S1]",
"foo=bar"
);
final CountDownLatch latch = new CountDownLatch(1);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(S1/foo = baz)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("S1", "foo", "baz", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|foo = baz|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Add new entries.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testAddNewEntries() throws Exception {
ConfigStore s = initStore("Foo.cfg"
);
final CountDownLatch latch = new CountDownLatch(2);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(k = vb)','SET(S1/k1 = v1b)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("", "k", "vb", null, null, null);
cm.setEntry("S1", "k1", "v1b", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("k = vb|[S1]|k1 = v1b|", cm.toString());
}
@Test
public void testAddNewEntriesWithAttributes() throws Exception {
ConfigStore s = initStore("Foo.cfg"
);
final CountDownLatch latch = new CountDownLatch(2);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(k^* = kb # C)','SET(S1/k1^* = k1b # C1)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("", "k", "kb", "^*", "C", Arrays.asList("#k"));
cm.setEntry("S1", "k1", "k1b", "^*", "C1", Arrays.asList("#k1"));
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#k|k^* = kb # C|[S1]|#k1|k1^* = k1b # C1|", cm.toString());
}
@Test
public void testAddExistingEntriesWithAttributes() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"#ka",
"k=va # Ca",
"#S1",
"[S1]",
"#k1a",
"k1=v1a # Cb"
);
final CountDownLatch latch = new CountDownLatch(2);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(k^* = kb # Cb)','SET(S1/k1^* = k1b # Cb1)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("", "k", "kb", "^*", "Cb", Arrays.asList("#kb"));
cm.setEntry("S1", "k1", "k1b", "^*", "Cb1", Arrays.asList("#k1b"));
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#kb|k^* = kb # Cb|#S1|[S1]|#k1b|k1^* = k1b # Cb1|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Remove existing entries.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testRemoveExistingEntries() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"k=v",
"[S1]",
"k1=v1"
);
final CountDownLatch latch = new CountDownLatch(2);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['REMOVE_ENTRY(k)','REMOVE_ENTRY(S1/k1)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.removeEntry("", "k");
cm.removeEntry("S1", "k1");
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|", cm.toString());
}
@Test
public void testRemoveExistingEntriesWithAttributes() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"#ka",
"k=va # Ca",
"#S1",
"[S1]",
"#k1a",
"k1=v1a # Cb"
);
final CountDownLatch latch = new CountDownLatch(2);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['REMOVE_ENTRY(k)','REMOVE_ENTRY(S1/k1)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.removeEntry("", "k");
cm.removeEntry("S1", "k1");
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#S1|[S1]|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Add new sections.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testAddNewSections() throws Exception {
ConfigStore s = initStore("Foo.cfg"
);
final CountDownLatch latch = new CountDownLatch(1);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(S3/k3 = v3)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setSection("", Arrays.asList("#D1"));
cm.setSection("S1", Arrays.asList("#S1"));
cm.setSection("S2", null);
cm.setSection("S3", Collections.<String>emptyList());
cm.setEntry("S3", "k3", "v3", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#D1||#S1|[S1]|[S2]|[S3]|k3 = v3|", cm.toString());
}
@Test
public void testModifyExistingSections() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"#Da",
"",
"#S1a",
"[S1]",
"[S2]",
"[S3]"
);
final CountDownLatch latch = new CountDownLatch(1);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(S3/k3 = v3)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setSection("", Arrays.asList("#Db"));
cm.setSection("S1", Arrays.asList("#S1b"));
cm.setSection("S2", null);
cm.setSection("S3", Collections.<String>emptyList());
cm.setEntry("S3", "k3", "v3", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#Db||#S1b|[S1]|[S2]|[S3]|k3 = v3|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Remove sections.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testRemoveSections() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"#Da",
"",
"k = v",
"",
"#S1",
"[S1]",
"#k1",
"k1 = v1",
"[S2]",
"#k2",
"k2 = v2",
"[S3]"
);
final CountDownLatch latch = new CountDownLatch(3);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['REMOVE_ENTRY(k)','REMOVE_ENTRY(S1/k1)','REMOVE_ENTRY(S2/k2)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.removeSection("");
cm.removeSection("S1");
cm.removeSection("S2");
cm.removeSection("S3");
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Update from store.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testUpdateFromStore() throws Exception {
ConfigStore s = initStore("Foo.cfg");
final CountDownLatch latch = new CountDownLatch(3);
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals("['SET(k = v # cv)','SET(S1/k1 = v1 # cv1)','SET(S2/k2 = v2 # cv2)']", events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
s.update("Foo.cfg",
"#Da",
"",
"k = v # cv",
"",
"#S1",
"[S1]",
"#k1",
"k1 = v1 # cv1",
"[S2]",
"#k2",
"k2 = v2 # cv2",
"[S3]"
);
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("#Da||k = v # cv||#S1|[S1]|#k1|k1 = v1 # cv1|[S2]|#k2|k2 = v2 # cv2|[S3]|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// Merges.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testMergeNoOverwrite() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"[S1]",
"k1 = v1a"
);
final CountDownLatch latch = new CountDownLatch(2);
final Queue<String> eventList = new ConcurrentLinkedQueue<>();
eventList.add("['SET(S1/k1 = v1b)']");
eventList.add("['SET(S2/k2 = v2b)']");
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals(eventList.poll(), events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("S2", "k2", "v2b", null, null, null);
s.update("Foo.cfg",
"[S1]",
"k1 = v1b"
);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|k1 = v1b|[S2]|k2 = v2b|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// If we're modifying an entry and it changes on the file system, we should overwrite the change on save().
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testMergeWithOverwrite() throws Exception {
ConfigStore s = initStore("Foo.cfg",
"[S1]",
"k1 = v1a"
);
final CountDownLatch latch = new CountDownLatch(2);
final Queue<String> eventList = new ConcurrentLinkedQueue<>();
eventList.add("['SET(S1/k1 = v1b)']");
eventList.add("['SET(S1/k1 = v1c)']");
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals(eventList.poll(), events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("S1", "k1", "v1c", null, null, null);
s.update("Foo.cfg",
"[S1]",
"k1 = v1b"
);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|k1 = v1c|", cm.toString());
}
//-----------------------------------------------------------------------------------------------------------------
// If the contents of a file have been modified on the file system before a signal has been received.
//-----------------------------------------------------------------------------------------------------------------
@Test
public void testMergeWithOverwriteNoSignal() throws Exception {
final Queue<String> contents = new ConcurrentLinkedQueue<>();
contents.add("[S1]\nk1 = v1a");
contents.add("[S1]\nk1 = v1b");
contents.add("[S1]\nk1 = v1c");
contents.add("[S1]\nk1 = v1c");
ConfigMemoryStore s = new ConfigMemoryStore(null) {
@Override
public synchronized String read(String name) {
return contents.poll();
}
};
try {
final CountDownLatch latch = new CountDownLatch(2);
final Queue<String> eventList = new ConcurrentLinkedQueue<>();
eventList.add("['SET(S1/k1 = v1b)']");
eventList.add("['SET(S1/k1 = v1c)']");
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals(eventList.poll(), events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("S1", "k1", "v1c", null, null, null);
cm.commit();
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|k1 = v1c|", cm.toString());
} finally {
s.close();
}
}
@Test
public void testMergeWithConstantlyUpdatingFile() throws Exception {
ConfigMemoryStore s = new ConfigMemoryStore(null) {
char c = 'a';
@Override
public synchronized String read(String name) {
return "[S1]\nk1 = v1" + (c++);
}
};
try {
final CountDownLatch latch = new CountDownLatch(10);
final Queue<String> eventList = new ConcurrentLinkedQueue<>();
eventList.add("['SET(S1/k1 = v1b)']");
eventList.add("['SET(S1/k1 = v1c)']");
eventList.add("['SET(S1/k1 = v1d)']");
eventList.add("['SET(S1/k1 = v1e)']");
eventList.add("['SET(S1/k1 = v1f)']");
eventList.add("['SET(S1/k1 = v1g)']");
eventList.add("['SET(S1/k1 = v1h)']");
eventList.add("['SET(S1/k1 = v1i)']");
eventList.add("['SET(S1/k1 = v1j)']");
eventList.add("['SET(S1/k1 = v1k)']");
LatchedListener l = new LatchedListener(latch) {
@Override
public void check(ConfigEvents events) throws Exception {
assertObjectEquals(eventList.poll(), events);
}
};
ConfigMap cm = s.getMap("Foo.cfg");
cm.register(l);
cm.setEntry("S1", "k1", "v1c", null, null, null);
try {
cm.commit();
fail("Exception expected.");
} catch (ConfigException e) {
assertEquals("Unable to store contents of config to store.", e.getMessage());
}
wait(latch);
assertNull(l.error);
cm.unregister(l);
assertTextEquals("[S1]|k1 = v1c|", cm.toString());
} finally {
s.close();
}
}
//-----------------------------------------------------------------------------------------------------------------
// Utilities.
//-----------------------------------------------------------------------------------------------------------------
private static ConfigStore initStore(String name, String...contents) {
return ConfigMemoryStore.create().build().update(name, contents);
}
public static class LatchedListener implements ConfigEventListener {
private final CountDownLatch latch;
private volatile String error = null;
public LatchedListener(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void onConfigChange(ConfigEvents events) {
try {
check(events);
} catch (Exception e) {
error = e.getLocalizedMessage();
}
for (int i = 0; i < events.size(); i++)
latch.countDown();
}
public void check(ConfigEvents events) throws Exception {
}
}
private static void wait(CountDownLatch latch) throws InterruptedException {
if (! latch.await(10, TimeUnit.SECONDS))
throw new RuntimeException("Latch failed.");
}
}