package nl.tudelft.simulation.dsol.naming.context.event;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.Hashtable;
import javax.naming.NamingException;
import org.djutils.event.EventInterface;
import org.djutils.event.EventListenerInterface;
import org.djutils.event.EventProducerInterface;
import org.djutils.event.ref.ReferenceType;
import org.junit.Test;
import nl.tudelft.simulation.dsol.naming.context.ContextTestUtil;
import nl.tudelft.simulation.naming.context.ContextInterface;
import nl.tudelft.simulation.naming.context.event.ContextEventProducerImpl;
import nl.tudelft.simulation.naming.context.event.ContextScope;
import nl.tudelft.simulation.naming.context.event.InitialEventContext;
import nl.tudelft.simulation.naming.context.util.ContextUtil;
/**
* EventContextTest.java.
*
* Copyright (c) 2020-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information https://simulation.tudelft.nl. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
*
* https://simulation.tudelft.nl/dsol/3.0/license.html.
*
* @author Alexander Verbraeck
*/
public class EventContextTest
{
/**
* Test the ContextEventProducerImpl class.
* @throws Exception on error when executing test
*/
@Test
public void testEventContextImplKeys() throws Exception
{
InitialEventContext ctx = null;
try
{
Hashtable environment = new Hashtable<>();
environment.put(InitialEventContext.INITIAL_CONTEXT_FACTORY,
"nl.tudelft.simulation.naming.context.JVMContextFactory");
ctx = InitialEventContext.instantiate(environment, "root");
// get the embedded contextEventProducer
Field cepiField = InitialEventContext.class.getDeclaredField("contextEventProducerImpl");
cepiField.setAccessible(true);
ContextEventProducerImpl contextEventProducerImpl = (ContextEventProducerImpl) cepiField.get(ctx);
// get the private makeRegistryKey and makeRegExpKey methods
Method makeRegistryKey =
ContextEventProducerImpl.class.getDeclaredMethod("makeRegistryKey", String.class, ContextScope.class);
makeRegistryKey.setAccessible(true);
Method makeRegExpKey =
ContextEventProducerImpl.class.getDeclaredMethod("makeRegex", String.class, ContextScope.class);
makeRegExpKey.setAccessible(true);
// OBJECT_SCOPE
String registryKeyObject = (String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1/object",
ContextScope.OBJECT_SCOPE);
assertEquals("/simulation1/sub1/object#OBJECT_SCOPE", registryKeyObject);
String regExpObject = (String) makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1/object",
ContextScope.OBJECT_SCOPE);
assertEquals("/simulation1/sub1/object", regExpObject);
assertTrue("/simulation1/sub1/object".matches(regExpObject));
assertFalse("/simulation1/sub1".matches(regExpObject));
assertFalse("/simulation1/sub1/object1".matches(regExpObject));
assertFalse("/simulation1/sub1/object/".matches(regExpObject));
assertFalse("root/simulation1/sub1/object".matches(regExpObject));
assertFalse("sub1/object".matches(regExpObject));
// LEVEL_SCOPE
registryKeyObject =
(String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1", ContextScope.LEVEL_SCOPE);
assertEquals("/simulation1/sub1#LEVEL_SCOPE", registryKeyObject);
registryKeyObject =
(String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1/", ContextScope.LEVEL_SCOPE);
assertEquals("/simulation1/sub1#LEVEL_SCOPE", registryKeyObject);
regExpObject =
(String) makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1", ContextScope.LEVEL_SCOPE);
assertEquals(regExpObject,
makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1/", ContextScope.LEVEL_SCOPE));
assertFalse("/simulation1/sub1".matches(regExpObject));
assertTrue("/simulation1/sub1/object".matches(regExpObject));
assertTrue("/simulation1/sub1/abc".matches(regExpObject));
assertFalse("/simulation1/sub1/abc/def".matches(regExpObject));
assertFalse("/simulation1/sub1/abc/".matches(regExpObject));
assertFalse("/simulation1".matches(regExpObject));
assertFalse("/simulation1/sub12".matches(regExpObject));
assertFalse("/simulation1/sub2".matches(regExpObject));
assertFalse("root/simulation1/sub1/object".matches(regExpObject));
assertFalse("sub1/object".matches(regExpObject));
// LEVEL_OBJECT_SCOPE
registryKeyObject = (String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1",
ContextScope.LEVEL_OBJECT_SCOPE);
assertEquals("/simulation1/sub1#LEVEL_OBJECT_SCOPE", registryKeyObject);
registryKeyObject = (String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1/",
ContextScope.LEVEL_OBJECT_SCOPE);
assertEquals("/simulation1/sub1#LEVEL_OBJECT_SCOPE", registryKeyObject);
regExpObject = (String) makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1",
ContextScope.LEVEL_OBJECT_SCOPE);
assertEquals(regExpObject,
makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1/", ContextScope.LEVEL_OBJECT_SCOPE));
assertTrue("/simulation1/sub1".matches(regExpObject));
assertTrue("/simulation1/sub1/".matches(regExpObject));
assertTrue("/simulation1/sub1/object".matches(regExpObject));
assertTrue("/simulation1/sub1/abc".matches(regExpObject));
assertFalse("/simulation1/sub1/abc/def".matches(regExpObject));
assertFalse("/simulation1/sub1/abc/".matches(regExpObject));
assertFalse("/simulation1".matches(regExpObject));
assertFalse("/simulation1/sub12".matches(regExpObject));
assertFalse("/simulation1/sub2".matches(regExpObject));
assertFalse("root/simulation1/sub1/object".matches(regExpObject));
assertFalse("sub1/object".matches(regExpObject));
// SUBTREE_SCOPE
registryKeyObject =
(String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1", ContextScope.SUBTREE_SCOPE);
assertEquals("/simulation1/sub1#SUBTREE_SCOPE", registryKeyObject);
registryKeyObject =
(String) makeRegistryKey.invoke(contextEventProducerImpl, "/simulation1/sub1/", ContextScope.SUBTREE_SCOPE);
assertEquals("/simulation1/sub1#SUBTREE_SCOPE", registryKeyObject);
regExpObject =
(String) makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1", ContextScope.SUBTREE_SCOPE);
assertEquals(regExpObject,
makeRegExpKey.invoke(contextEventProducerImpl, "/simulation1/sub1/", ContextScope.SUBTREE_SCOPE));
assertTrue("/simulation1/sub1".matches(regExpObject));
assertTrue("/simulation1/sub1/".matches(regExpObject));
assertTrue("/simulation1/sub1/object".matches(regExpObject));
assertTrue("/simulation1/sub1/abc".matches(regExpObject));
assertTrue("/simulation1/sub1/abc/def".matches(regExpObject));
assertTrue("/simulation1/sub1/abc/".matches(regExpObject));
assertFalse("/simulation1".matches(regExpObject));
assertFalse("/simulation1/sub12".matches(regExpObject));
assertFalse("/simulation1/sub2/sub1".matches(regExpObject));
assertFalse("root/simulation1/sub1/object".matches(regExpObject));
assertFalse("sub1/object".matches(regExpObject));
}
catch (NamingException | RemoteException | NoSuchFieldException | SecurityException | IllegalArgumentException
| IllegalAccessException exception)
{
throw exception;
}
finally
{
ContextTestUtil.destroyInitialEventContext(ctx);
}
}
/**
* Test the context scope pub/sub mechanism of the EventContext.
* @throws Exception on error when executing test
*/
@Test
public void testEventContextPubSub() throws Exception
{
InitialEventContext context = null;
try
{
Hashtable environment = new Hashtable<>();
environment.put(InitialEventContext.INITIAL_CONTEXT_FACTORY,
"nl.tudelft.simulation.naming.context.JVMContextFactory");
context = InitialEventContext.instantiate(environment, "root");
// make the start of a tree
ContextInterface c11 = context.createSubcontext("/1/11");
ContextInterface c12 = context.createSubcontext("/1/12");
context.createSubcontext("/2/21/211/2111/21111");
TestObject object111 = new TestObject("object111");
c11.bind("o111", object111);
ContextInterface c1 = ContextUtil.lookupSubContext(context, "/1");
TestEventListener listenerObject = new TestEventListener(ContextScope.OBJECT_SCOPE);
TestEventListener listenerLevel = new TestEventListener(ContextScope.LEVEL_SCOPE);
TestEventListener listenerLevelObject = new TestEventListener(ContextScope.LEVEL_OBJECT_SCOPE);
TestEventListener listenerSubTree = new TestEventListener(ContextScope.SUBTREE_SCOPE);
context.addListener(listenerObject, "/1/11/o111", ContextScope.OBJECT_SCOPE);
context.addListener(listenerLevel, "/1/11", ContextScope.LEVEL_SCOPE, ReferenceType.WEAK);
context.addListener(listenerLevelObject, "/1/11", ContextScope.LEVEL_OBJECT_SCOPE,
EventProducerInterface.LAST_POSITION);
context.addListener(listenerSubTree, "/1/11", ContextScope.SUBTREE_SCOPE, EventProducerInterface.FIRST_POSITION,
ReferenceType.STRONG);
// add an object to c11. Expected behavior: O: no, L: yes, LO: yes, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(true);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
TestObject object112 = new TestObject("object112");
c11.bind("o112", object112);
EventInterface eventLevel = listenerLevel.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_ADDED_EVENT, eventLevel.getType());
assertTrue(eventLevel.getContent() instanceof Object[]);
Object[] content = (Object[]) eventLevel.getContent();
assertEquals(3, content.length);
assertEquals(c11.getAbsolutePath(), content[0]);
assertEquals("o112", content[1]);
assertEquals(object112, content[2]);
assertEquals(c11.getSourceId(), eventLevel.getSourceId());
EventInterface eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventLevelObject);
EventInterface eventSubTree = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventSubTree);
// add an object to c12. Expected behavior: O: no, L: no, LO: no, ST: no
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(false);
TestObject object121 = new TestObject("object121");
c12.bind("o121", object121);
// add a context to c11. Expected behavior: O: no, L: yes, LO: yes, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(true);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
ContextInterface c111 = context.createSubcontext("/1/11/111");
eventLevel = listenerLevel.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_ADDED_EVENT, eventLevel.getType());
assertTrue(eventLevel.getContent() instanceof Object[]);
content = (Object[]) eventLevel.getContent();
assertEquals(3, content.length);
assertEquals(c11.getAbsolutePath(), content[0]);
assertEquals("111", content[1]);
assertEquals(c111, content[2]);
assertEquals(c11.getSourceId(), eventLevel.getSourceId());
eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventLevelObject);
eventSubTree = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventSubTree);
// add an object to c111. Expected behavior: O: no, L: no, LO: no, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(true);
TestObject object111x = new TestObject("object111x");
c111.bind("o111x", object111x);
eventSubTree = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_ADDED_EVENT, eventSubTree.getType());
assertTrue(eventSubTree.getContent() instanceof Object[]);
content = (Object[]) eventSubTree.getContent();
assertEquals(3, content.length);
assertEquals(c111.getAbsolutePath(), content[0]);
assertEquals("o111x", content[1]);
assertEquals(object111x, content[2]);
assertEquals(c111.getSourceId(), eventSubTree.getSourceId());
// add an subcontext to c111. Expected behavior: O: no, L: no, LO: no, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(true);
ContextInterface c1111 = context.createSubcontext("/1/11/111/1111");
eventSubTree = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_ADDED_EVENT, eventSubTree.getType());
assertTrue(eventSubTree.getContent() instanceof Object[]);
content = (Object[]) eventSubTree.getContent();
assertEquals(3, content.length);
assertEquals(c111.getAbsolutePath(), content[0]);
assertEquals("1111", content[1]);
assertEquals(c1111, content[2]);
assertEquals(c111.getSourceId(), eventSubTree.getSourceId());
// change an object in c11. Expected behavior: O: no, L: yes, LO: yes, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(true);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
c11.fireObjectChangedEventKey("o112");
eventLevel = listenerLevel.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_CHANGED_EVENT, eventLevel.getType());
assertTrue(eventLevel.getContent() instanceof Object[]);
content = (Object[]) eventLevel.getContent();
assertEquals(3, content.length);
assertEquals(c11.getAbsolutePath(), content[0]);
assertEquals("o112", content[1]);
assertEquals(object112, content[2]);
assertEquals(c11.getSourceId(), eventLevel.getSourceId());
eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventLevelObject);
eventSubTree = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventSubTree);
// change an object in c12. Expected behavior: O: no, L: no, LO: no, ST: no
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(false);
c12.fireObjectChangedEventKey("o121");
// change an object in c111. Expected behavior: O: no, L: no, LO: no, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(true);
c111.fireObjectChangedEventKey("o111x");
eventSubTree = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_CHANGED_EVENT, eventSubTree.getType());
assertTrue(eventSubTree.getContent() instanceof Object[]);
content = (Object[]) eventSubTree.getContent();
assertEquals(3, content.length);
assertEquals(c111.getAbsolutePath(), content[0]);
assertEquals("o111x", content[1]);
assertEquals(object111x, content[2]);
assertEquals(c111.getSourceId(), eventSubTree.getSourceId());
// remove an object from c12. Expected behavior: O: no, L: no, LO: no, ST: no
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(false);
c12.unbindObject("o121");
// remove an object from c111. Expected behavior: O: no, L: no, LO: no, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(true);
c111.unbindObject("o111x");
eventSubTree = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_REMOVED_EVENT, eventSubTree.getType());
assertTrue(eventSubTree.getContent() instanceof Object[]);
content = (Object[]) eventSubTree.getContent();
assertEquals(3, content.length);
assertEquals(c111.getAbsolutePath(), content[0]);
assertEquals("o111x", content[1]);
assertEquals(object111x, content[2]);
assertEquals(c111.getSourceId(), eventSubTree.getSourceId());
// remove object o111. Expected behavior: O: yes, L: yes, LO: yes, ST: yes
listenerObject.setExpectingNotification(true);
listenerLevel.setExpectingNotification(true);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
c11.unbindObject("o111");
eventLevel = listenerLevel.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_REMOVED_EVENT, eventLevel.getType());
assertTrue(eventLevel.getContent() instanceof Object[]);
content = (Object[]) eventLevel.getContent();
assertEquals(3, content.length);
assertEquals(c11.getAbsolutePath(), content[0]);
assertEquals("o111", content[1]);
assertEquals(object111, content[2]);
assertEquals(c11.getSourceId(), eventLevel.getSourceId());
eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventLevelObject);
eventSubTree = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventSubTree);
EventInterface eventObject = listenerObject.getReceivedEvent();
assertEquals(eventLevel, eventObject);
// remove context 111. Expected behavior: O: no, L: yes, LO: yes, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(true);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
ContextUtil.destroySubContext(c11, "111");
eventLevel = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_REMOVED_EVENT, eventLevel.getType());
assertTrue(eventLevel.getContent() instanceof Object[]);
content = (Object[]) eventLevel.getContent();
assertEquals(3, content.length);
assertEquals(c11.getAbsolutePath(), content[0]);
assertEquals("111", content[1]);
assertEquals(c111, content[2]);
assertEquals(c11.getSourceId(), eventLevel.getSourceId());
eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventLevelObject);
eventSubTree = listenerLevelObject.getReceivedEvent();
assertEquals(eventLevel, eventSubTree);
// empty c11
c11.unbindObject("o112");
assertEquals(0, c11.bindings().size());
// remove context 11. Expected behavior: O: no, L: no, LO: yes, ST: yes
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(true);
listenerSubTree.setExpectingNotification(true);
ContextUtil.destroySubContext(c1, "11");
eventSubTree = listenerSubTree.getReceivedEvent();
assertEquals(ContextInterface.OBJECT_REMOVED_EVENT, eventSubTree.getType());
assertTrue(eventSubTree.getContent() instanceof Object[]);
content = (Object[]) eventSubTree.getContent();
assertEquals(3, content.length);
assertEquals(c1.getAbsolutePath(), content[0]);
assertEquals("11", content[1]);
assertEquals(c11, content[2]);
assertEquals(c1.getSourceId(), eventSubTree.getSourceId());
eventLevelObject = listenerLevelObject.getReceivedEvent();
assertEquals(eventSubTree, eventLevelObject);
// remove context /1. There is nothing left to report. Expected behavior: O: no, L: no, LO: no, ST: no
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(false);
System.out.println("\nSHOULD HAVE 1 and 2");
System.out.println(context.toString(true));
context.destroySubcontext("1");
// remove context /2. This should not report anything. Expected behavior: O: no, L: no, LO: no, ST: no
listenerObject.setExpectingNotification(false);
listenerLevel.setExpectingNotification(false);
listenerLevelObject.setExpectingNotification(false);
listenerSubTree.setExpectingNotification(false);
context.destroySubcontext("/2");
context.removeListener(listenerObject, "/1/11/o111", ContextScope.OBJECT_SCOPE);
context.removeListener(listenerLevel, "/1/11", ContextScope.LEVEL_SCOPE);
context.removeListener(listenerLevelObject, "/1/11", ContextScope.LEVEL_OBJECT_SCOPE);
context.removeListener(listenerSubTree, "/1/11", ContextScope.SUBTREE_SCOPE);
}
catch (NamingException | RemoteException | SecurityException | IllegalArgumentException exception)
{
throw exception;
}
finally
{
ContextTestUtil.destroyInitialEventContext(context);
}
}
/** EventListener to test received events. */
protected static class TestEventListener implements EventListenerInterface
{
/** */
private static final long serialVersionUID = 20191230L;
/** expect notification or not. */
private boolean expectingNotification = true;
/** received event. */
private EventInterface receivedEvent;
/** the scope for reporting. */
private final ContextScope contextScope;
/**
* @param contextScope the context scope for reporting
*/
public TestEventListener(final ContextScope contextScope)
{
this.contextScope = contextScope;
}
/**
* @param expectingNotification set expectingNotification
*/
public void setExpectingNotification(final boolean expectingNotification)
{
this.expectingNotification = expectingNotification;
this.receivedEvent = null;
}
/**
* @return receivedEvent
*/
public EventInterface getReceivedEvent()
{
return this.receivedEvent;
}
/** {@inheritDoc} */
@Override
public void notify(final EventInterface event) throws RemoteException
{
if (!this.expectingNotification)
{
fail("Received event " + event + " unexpectedly for scope " + this.contextScope);
}
this.receivedEvent = event;
}
}
/** Test object with ToString() method. */
protected static class TestObject implements Serializable
{
/** */
private static final long serialVersionUID = 1L;
/** */
private String field;
/**
* @param field the field
*/
public TestObject(String field)
{
this.field = field;
}
/**
* @return field
*/
public String getField()
{
return this.field;
}
/**
* @param field set field
*/
public void setField(final String field)
{
this.field = field;
}
/** {@inheritDoc} */
@Override
public String toString()
{
return this.field;
}
}
}