Add MockForwarder
Allows NDN applications to test interest/data exchanges as if an NFD was present... but with no network IO
diff --git a/src/main/java/com/intel/jndn/mock/MockFace.java b/src/main/java/com/intel/jndn/mock/MockFace.java
index 8980fa7..4f73a02 100644
--- a/src/main/java/com/intel/jndn/mock/MockFace.java
+++ b/src/main/java/com/intel/jndn/mock/MockFace.java
@@ -77,7 +77,7 @@
public final List<SignalOnSendData> onSendData = new ArrayList<>();
private static final Logger LOGGER = Logger.getLogger(MockFace.class.getName());
- private MockFaceTransport transport;
+ private MockTransport transport;
private KeyChain keyChain;
/////////////////////////////////////////////////////////////////////////////
@@ -175,7 +175,7 @@
* new MockFace(new Options());
* // use onSendInterest.add(handler) and onSendData.add(handler)
* // to add custom logic when Interest or Data packet are sent
- * // from the upper level (to transport)
+ * // from the upper level (to callback)
* </pre>
*
* To create Face that just logs packets in sentInterests and sentData:
@@ -186,8 +186,8 @@
* @param options see {@link Options}
*/
public MockFace(final Options options) {
- super(new MockFaceTransport(), null);
- transport = (MockFaceTransport) node_.getTransport();
+ super(new MockTransport(), null);
+ transport = (MockTransport) node_.getTransport();
transport.setOnSendBlock(new OnIncomingPacket());
try {
@@ -222,7 +222,7 @@
/**
* Route incoming packets to the correct callbacks.
*/
- private class OnIncomingPacket implements MockFaceTransport.OnSendBlockSignal {
+ private class OnIncomingPacket implements MockTransport.OnSendBlockSignal {
/**
* {@inheritDoc}
*/
@@ -252,7 +252,7 @@
LOGGER.info("Received an unknown packet");
}
} catch (EncodingException e) {
- LOGGER.log(Level.INFO, "Failed to decode incoming packet", e);
+ LOGGER.log(Level.INFO, "Failed to decodeParameters incoming packet", e);
}
}
}
@@ -309,7 +309,7 @@
}
/**
- * Mock reception of the Interest packet on the Face (from transport).
+ * Mock reception of the Interest packet on the Face (from callback).
*
* @param interest the mock-remote interest to add to the PIT
* @throws EncodingException if packet encoding fails (it should not)
@@ -319,7 +319,7 @@
}
/**
- * Mock reception of the Data packet on the Face (from transport).
+ * Mock reception of the Data packet on the Face (from callback).
*
* @param data the mock-remote data to add to the CS
* @throws EncodingException if packet encoding fails (it should not)
@@ -329,7 +329,7 @@
}
/**
- * @return the transport for this face
+ * @return the callback for this face
*/
public Transport getTransport() {
return transport;
diff --git a/src/main/java/com/intel/jndn/mock/MockForwarder.java b/src/main/java/com/intel/jndn/mock/MockForwarder.java
new file mode 100644
index 0000000..dffc321
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/MockForwarder.java
@@ -0,0 +1,154 @@
+package com.intel.jndn.mock;
+
+import com.intel.jndn.mock.forwarder.BufferHandler;
+import com.intel.jndn.mock.forwarder.FibImpl;
+import com.intel.jndn.mock.forwarder.LocalFibEntry;
+import com.intel.jndn.mock.forwarder.OnPrefixRegistration;
+import com.intel.jndn.mock.forwarder.PitImpl;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import net.named_data.jndn.transport.Transport;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class MockForwarder {
+ private static final Logger LOGGER = Logger.getLogger(MockForwarder.class.getName());
+ private final KeyChain keyChain;
+ private final Name certName;
+ private final Fib fib = new FibImpl();
+ private final Pit pit = new PitImpl();
+
+ /**
+ * Forwarding information base API; use this for recording FIB entries
+ */
+ public interface Fib {
+ /**
+ * @param interest the incoming client interest
+ * @return all FIB entries matching the interest
+ */
+ Collection<FibEntry> find(Interest interest);
+
+ /**
+ * @param entry a new FIB entry to add; enables routing to the face (or more correctly, channel) contained in this
+ * entry
+ */
+ void add(FibEntry entry);
+ }
+
+ /**
+ * Entry in the FIB; use this for forwarding interest packets
+ */
+ public interface FibEntry {
+ /**
+ * @param interest the interest to forward
+ * @param sourceTransport the source of the interest for use in the callback
+ */
+ void forward(Interest interest, Transport sourceTransport);
+
+ /**
+ * @return then entry prefix name
+ */
+ Name getPrefix();
+
+ /**
+ * @return the entry flags
+ */
+ ForwardingFlags getFlags();
+ }
+
+ /**
+ * Pending interest table API; use this for recording incoming interests
+ */
+ public interface Pit {
+ /**
+ * @param entry the PIT entry to add
+ */
+ void add(PitEntry entry);
+
+ /**
+ * @param interest the incoming interest to match against
+ * @return true if the interest matches an entry already in the PIT
+ */
+ boolean has(Interest interest);
+
+ /**
+ * @param name the name to match against
+ * @return the PIT entries matching a name, removing them from the PIT
+ */
+ Collection<PitEntry> extract(Name name);
+ }
+
+ /**
+ * Entry in the PIT; use this for forwarding data packets
+ */
+ public interface PitEntry {
+ /**
+ * @param data the packet to forward
+ */
+ void forward(Data data);
+
+ /**
+ * @return the interest that first created the entry
+ */
+ Interest getInterest();
+
+ /**
+ * @return true if the entry has been satisfied (has had a matching data forwarded through it)
+ */
+ boolean isSatisfied();
+ }
+
+ /**
+ * Mock-specific API for recording the source and destination of incoming interests
+ */
+ public interface OnInterestReceived {
+ void in(Interest interest, Transport destinationTransport, Face sourceFace);
+ }
+
+ public MockForwarder() {
+ try {
+ keyChain = MockKeyChain.configure(new Name("/mock/forwarder"));
+ certName = keyChain.getDefaultCertificateName();
+ } catch (SecurityException e) {
+ throw new IllegalStateException("Failed to set up mock prefix registration", e);
+ }
+
+ OnPrefixRegistration onPrefixRegistration = new OnPrefixRegistration(keyChain, fib);
+ Name registrationPrefix = new Name("/localhost/nfd/rib/register");
+ register(registrationPrefix, onPrefixRegistration, new ForwardingFlags());
+ }
+
+ public Face connect() {
+ MockForwarderFace face = new MockForwarderFace();
+ face.setCommandSigningInfo(keyChain, certName);
+ LOGGER.info("Connected face with: " + face.getTransport());
+ return face;
+ }
+
+ public void register(Name prefix, OnInterestReceived callback, ForwardingFlags flags) {
+ Face registrationFace = this.connect();
+ FibEntry registrationEntry = new LocalFibEntry(prefix, callback, registrationFace, flags);
+ fib.add(registrationEntry);
+ }
+
+ private class MockForwarderFace extends Face {
+ MockForwarderFace() {
+ super(new MockTransport(), null);
+ MockTransport transport = (MockTransport) node_.getTransport();
+ transport.setOnSendBlock(new BufferHandler(transport, fib, pit));
+ }
+
+ Transport getTransport() {
+ return node_.getTransport();
+ }
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/MockTransport.java b/src/main/java/com/intel/jndn/mock/MockTransport.java
index df437e0..613aacf 100644
--- a/src/main/java/com/intel/jndn/mock/MockTransport.java
+++ b/src/main/java/com/intel/jndn/mock/MockTransport.java
@@ -31,9 +31,9 @@
* @author Alexander Afanasyev, <aa@cs.ucla.edu>
* @author Andrew Brown <andrew.brown@intel.com>
*/
-class MockFaceTransport extends Transport {
+public class MockTransport extends Transport {
private OnSendBlockSignal onSendBlock;
- private static final Logger LOGGER = Logger.getLogger(MockFaceTransport.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(MockTransport.class.getName());
private boolean connected;
private ElementReader elementReader;
private final List<ByteBuffer> receiveBuffer = new LinkedList<>();
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/BufferHandler.java b/src/main/java/com/intel/jndn/mock/forwarder/BufferHandler.java
new file mode 100644
index 0000000..4347075
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/BufferHandler.java
@@ -0,0 +1,87 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import com.intel.jndn.mock.MockTransport;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.TlvWireFormat;
+import net.named_data.jndn.encoding.tlv.Tlv;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.transport.Transport;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class BufferHandler implements MockTransport.OnSendBlockSignal {
+
+ private static final Logger LOGGER = Logger.getLogger(BufferHandler.class.getName());
+ private final MockTransport transport;
+ private final MockForwarder.Fib fib;
+ private final MockForwarder.Pit pit;
+
+ public BufferHandler(MockTransport transport, MockForwarder.Fib fib, MockForwarder.Pit pit) {
+ this.transport = transport;
+ this.fib = fib;
+ this.pit = pit;
+ }
+
+ @Override
+ public void emit(ByteBuffer buffer) {
+ try {
+ if (isInterest(buffer) || isData(buffer)) {
+ TlvDecoder decoder = new TlvDecoder(buffer);
+ if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
+ Interest interest = new Interest();
+ interest.wireDecode(buffer, TlvWireFormat.get());
+ forward(interest, transport);
+ } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
+ Data data = new Data();
+ data.wireDecode(buffer, TlvWireFormat.get());
+ forward(data);
+ }
+ } else {
+ LOGGER.warning("Received an unknown packet");
+ }
+ } catch (EncodingException e) {
+ LOGGER.log(Level.INFO, "Failed to decodeParameters incoming packet", e);
+ }
+ }
+
+ private boolean isInterest(ByteBuffer buffer) {
+ return buffer.get(0) == Tlv.Interest;
+ }
+
+ private boolean isData(ByteBuffer buffer) {
+ return buffer.get(0) == Tlv.Data;
+ }
+
+ private void forward(Interest interest, Transport transport) {
+ if (pit.has(interest)) {
+ LOGGER.info("Already seen interest, swallowing: " + interest.toUri());
+ return;
+ }
+
+ LOGGER.info("Adding interest to PIT: " + interest.toUri());
+ pit.add(new PitEntryImpl(interest, (MockTransport) transport));
+
+ LOGGER.info("Forwarding interest: " + interest.toUri());
+ for (MockForwarder.FibEntry entry : fib.find(interest)) {
+ entry.forward(interest, transport);
+ }
+ }
+
+ private void forward(Data data) {
+ Collection<MockForwarder.PitEntry> found = pit.extract(data.getName());
+ LOGGER.log(Level.INFO, "Found {0} pending interests", found.size());
+
+ for (MockForwarder.PitEntry pendingInterest : found) {
+ pendingInterest.forward(data);
+ }
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/ClientFibEntry.java b/src/main/java/com/intel/jndn/mock/forwarder/ClientFibEntry.java
new file mode 100644
index 0000000..8322ded
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/ClientFibEntry.java
@@ -0,0 +1,43 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import com.intel.jndn.mock.MockTransport;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.transport.Transport;
+
+import java.util.logging.Logger;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+class ClientFibEntry implements MockForwarder.FibEntry {
+
+ private static final Logger LOGGER = Logger.getLogger(ClientFibEntry.class.getName());
+ private final Name prefix;
+ private final MockTransport transport;
+ private final ForwardingFlags flags;
+
+ ClientFibEntry(Name prefix, MockTransport transport, ForwardingFlags flags) {
+ this.prefix = prefix;
+ this.transport = transport;
+ this.flags = flags;
+ }
+
+ @Override
+ public void forward(Interest interest, Transport sourceTransport) {
+ LOGGER.info("Receiving interest on: " + this.transport);
+ transport.receive(interest.wireEncode().buf());
+ }
+
+ @Override
+ public Name getPrefix() {
+ return new Name(prefix);
+ }
+
+ @Override
+ public ForwardingFlags getFlags() {
+ return flags;
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/FibImpl.java b/src/main/java/com/intel/jndn/mock/forwarder/FibImpl.java
new file mode 100644
index 0000000..7c87b61
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/FibImpl.java
@@ -0,0 +1,37 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class FibImpl implements MockForwarder.Fib {
+
+ private final ConcurrentHashMap<Name, MockForwarder.FibEntry> fib = new ConcurrentHashMap<>();
+
+ @Override
+ public void add(MockForwarder.FibEntry entry) {
+ fib.put(entry.getPrefix(), entry);
+ }
+
+ public List<MockForwarder.FibEntry> find(Interest interest) {
+ ArrayList<MockForwarder.FibEntry> entries = new ArrayList<>();
+ for (int i = interest.getName().size(); i >= 0; i--) {
+ Name prefix = interest.getName().getPrefix(i);
+ MockForwarder.FibEntry entry = fib.get(prefix);
+ if (entry != null) {
+ entries.add(entry);
+ if (!entry.getFlags().getChildInherit() || entry.getFlags().getCapture()) {
+ break;
+ }
+ }
+ }
+ return entries;
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/LocalFibEntry.java b/src/main/java/com/intel/jndn/mock/forwarder/LocalFibEntry.java
new file mode 100644
index 0000000..5c0da0e
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/LocalFibEntry.java
@@ -0,0 +1,44 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.transport.Transport;
+
+import java.util.logging.Logger;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class LocalFibEntry implements MockForwarder.FibEntry {
+
+ private static final Logger LOGGER = Logger.getLogger(LocalFibEntry.class.getName());
+ private final Name prefix;
+ private final MockForwarder.OnInterestReceived callback;
+ private final Face registrationFace;
+ private final ForwardingFlags flags;
+
+ public LocalFibEntry(Name prefix, MockForwarder.OnInterestReceived callback, Face registrationFace, ForwardingFlags flags) {
+ this.prefix = prefix;
+ this.callback = callback;
+ this.registrationFace = registrationFace;
+ this.flags = flags;
+ }
+
+ public void forward(Interest interest, Transport sourceTransport) {
+ LOGGER.info("Forwarding interest on: " + this.callback);
+ callback.in(interest, sourceTransport, registrationFace);
+ }
+
+ @Override
+ public Name getPrefix() {
+ return new Name(prefix);
+ }
+
+ @Override
+ public ForwardingFlags getFlags() {
+ return flags;
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/OnPrefixRegistration.java b/src/main/java/com/intel/jndn/mock/forwarder/OnPrefixRegistration.java
new file mode 100644
index 0000000..f213789
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/OnPrefixRegistration.java
@@ -0,0 +1,89 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import com.intel.jndn.mock.MockTransport;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.ControlResponse;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import net.named_data.jndn.transport.Transport;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Handle prefix registration requests from clients to a mock forwarder; must conform to specification outlined in
+ * https://redmine.named-data.net/projects/nfd/wiki/RibMgmt.
+ *
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class OnPrefixRegistration implements MockForwarder.OnInterestReceived {
+ private static final Logger LOGGER = Logger.getLogger(OnPrefixRegistration.class.getName());
+ private static final int STATUS_CODE_OK = 200;
+ private static final int CONTROL_PARAMETERS_NAME_OFFSET = -5;
+ private static final int CONTROL_COMMAND_NAME_OFFSET = 3;
+ private final KeyChain keyChain;
+ private final MockForwarder.Fib fib;
+
+ public OnPrefixRegistration(KeyChain keyChain, MockForwarder.Fib fib) {
+ this.keyChain = keyChain;
+ this.fib = fib;
+ }
+
+ @Override
+ public void in(Interest interest, Transport destinationTransport, Face localFace) {
+ LOGGER.info("Received registration request: " + interest.toUri());
+ ControlParameters params = decodeParameters(interest);
+
+ MockForwarder.FibEntry entry = new ClientFibEntry(params.getName(), (MockTransport) destinationTransport, params.getForwardingFlags());
+ fib.add(entry);
+ LOGGER.info("Added new route " + params.getName() + " to: " + destinationTransport);
+
+ ControlResponse response = encodeResponse(params);
+
+ Data data = new Data();
+ data.setName(interest.getName());
+ data.setContent(response.wireEncode());
+ signResponse(data);
+
+ try {
+ localFace.putData(data);
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Failed to send registration response", e);
+ }
+ }
+
+ private ControlParameters decodeParameters(Interest interest) {
+ ControlParameters params = new ControlParameters();
+ try {
+ params.wireDecode(interest.getName().get(CONTROL_PARAMETERS_NAME_OFFSET).getValue());
+ params.setFaceId(1);
+ params.setOrigin(0);
+ params.setCost(0);
+ } catch (EncodingException e) {
+ throw new IllegalArgumentException("", e);
+ }
+ return params;
+ }
+
+ private ControlResponse encodeResponse(ControlParameters params) {
+ ControlResponse response = new ControlResponse();
+ response.setStatusCode(STATUS_CODE_OK);
+ response.setStatusText("OK");
+ response.setBodyAsControlParameters(params);
+ return response;
+ }
+
+ private void signResponse(Data data) {
+ try {
+ keyChain.sign(data);
+ } catch (SecurityException e) {
+ LOGGER.log(Level.FINE, "MockKeyChain signing failed", e);
+ }
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/PitEntryImpl.java b/src/main/java/com/intel/jndn/mock/forwarder/PitEntryImpl.java
new file mode 100644
index 0000000..74b9d7e
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/PitEntryImpl.java
@@ -0,0 +1,43 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import com.intel.jndn.mock.MockTransport;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+
+import java.util.logging.Logger;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+final class PitEntryImpl implements MockForwarder.PitEntry {
+
+ private static final Logger LOGGER = Logger.getLogger(PitEntryImpl.class.getName());
+ public final Interest interest;
+ private final MockTransport transport;
+ private boolean satisfied = false;
+
+ PitEntryImpl(Interest interest, MockTransport transport) {
+ this.interest = interest;
+ this.transport = transport;
+ }
+
+ public void forward(Data data) {
+ LOGGER.info("Forwarding data on: " + this.transport);
+
+ if (satisfied) {
+ LOGGER.warning("Data already forwarded for PIT entry: " + interest.toUri());
+ }
+
+ transport.receive(data.wireEncode().buf());
+ satisfied = true;
+ }
+
+ public Interest getInterest() {
+ return new Interest(interest);
+ }
+
+ public boolean isSatisfied() {
+ return satisfied;
+ }
+}
diff --git a/src/main/java/com/intel/jndn/mock/forwarder/PitImpl.java b/src/main/java/com/intel/jndn/mock/forwarder/PitImpl.java
new file mode 100644
index 0000000..3e7a476
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/forwarder/PitImpl.java
@@ -0,0 +1,53 @@
+package com.intel.jndn.mock.forwarder;
+
+import com.intel.jndn.mock.MockForwarder;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class PitImpl implements MockForwarder.Pit {
+
+ private final Map<Name, List<MockForwarder.PitEntry>> pit = new ConcurrentHashMap<>();
+
+ public List<MockForwarder.PitEntry> extract(Name name) {
+ ArrayList<MockForwarder.PitEntry> entries = new ArrayList<>();
+ for (int i = name.size(); i >= 0; i--) {
+ Name prefix = name.getPrefix(i);
+ List<MockForwarder.PitEntry> pendingInterests = pit.get(prefix);
+ if (pendingInterests != null) {
+ entries.addAll(pendingInterests);
+ pendingInterests.clear(); // TODO is this necessary
+ }
+ }
+ return entries;
+ }
+
+ public void add(MockForwarder.PitEntry entry) {
+ if (!pit.containsKey(entry.getInterest().getName())) {
+ pit.put(entry.getInterest().getName(), new ArrayList<MockForwarder.PitEntry>(1));
+ }
+ pit.get(entry.getInterest().getName()).add(entry);
+ }
+
+ public boolean has(Interest interest) {
+ List<MockForwarder.PitEntry> entries = pit.get(interest.getName());
+
+ // TODO simplify
+ if (entries != null && entries.size() > 0) {
+ return true;
+// for(int i = 0; i < entries.size(); i++){
+// if(entries.get(i).interest.equals(interest)){
+// return true;
+// }
+// }
+ }
+ return false;
+ }
+}
diff --git a/src/test/java/com/intel/jndn/mock/MockFaceTest.java b/src/test/java/com/intel/jndn/mock/MockFaceTest.java
index 7c8bbc9..37b0bca 100644
--- a/src/test/java/com/intel/jndn/mock/MockFaceTest.java
+++ b/src/test/java/com/intel/jndn/mock/MockFaceTest.java
@@ -181,7 +181,7 @@
}
final State state = new State();
- // connect transport
+ // connect callback
face.registerPrefix(new Name("/fake/prefix"), (OnInterestCallback) null, new OnRegisterFailed() {
@Override
public void onRegisterFailed(final Name prefix) {
diff --git a/src/test/java/com/intel/jndn/mock/MockForwarderTest.java b/src/test/java/com/intel/jndn/mock/MockForwarderTest.java
new file mode 100644
index 0000000..3d5e0ba
--- /dev/null
+++ b/src/test/java/com/intel/jndn/mock/MockForwarderTest.java
@@ -0,0 +1,92 @@
+package com.intel.jndn.mock;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.InterestFilter;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnData;
+import net.named_data.jndn.OnInterestCallback;
+import net.named_data.jndn.OnRegisterFailed;
+import net.named_data.jndn.OnRegisterSuccess;
+import net.named_data.jndn.OnTimeout;
+import net.named_data.jndn.security.KeyChain;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Andrew Brown, andrew.brown@intel.com
+ */
+public class MockForwarderTest {
+ private static final Logger LOGGER = Logger.getLogger(MockForwarderTest.class.getName());
+
+ @Test
+ public void usage() throws Exception {
+ Name prefix = new Name("/test");
+ MockForwarder forwarder = new MockForwarder();
+ Face a = forwarder.connect();
+ Face b = forwarder.connect();
+
+ LOGGER.info("Registering prefix: " + prefix);
+ final CountDownLatch response1 = new CountDownLatch(1);
+ a.registerPrefix(prefix, new OnInterestCallback() {
+ @Override
+ public void onInterest(Name prefix, Interest interest, Face face, long interestFilterId, InterestFilter filter) {
+ LOGGER.info("Received interest: " + interest.toUri());
+ try {
+ face.putData(new Data(interest.getName()));
+ LOGGER.info("Sent data to interest: " + interest.getName());
+ } catch (IOException e) {
+ LOGGER.info("Failed to send data for: " + interest.toUri());
+ }
+ }
+ }, new OnRegisterFailed() {
+ @Override
+ public void onRegisterFailed(Name prefix) {
+ LOGGER.severe("Failed to register prefix for: " + prefix);
+ response1.countDown();
+ }
+ }, new OnRegisterSuccess() {
+ @Override
+ public void onRegisterSuccess(Name prefix, long registeredPrefixId) {
+ LOGGER.info("Prefix registered: " + prefix);
+ response1.countDown();
+ }
+ });
+ a.processEvents();
+ response1.await(1, TimeUnit.SECONDS);
+ assertEquals(0, response1.getCount());
+
+ LOGGER.info("Sending interest to prefix: " + prefix);
+ final CountDownLatch response2 = new CountDownLatch(1);
+ final AtomicBoolean received = new AtomicBoolean(false);
+ b.expressInterest(prefix, new OnData() {
+ @Override
+ public void onData(Interest interest, Data data) {
+ LOGGER.info("Received data: " + data.getName());
+ response2.countDown();
+ received.set(true);
+ }
+ }, new OnTimeout() {
+ @Override
+ public void onTimeout(Interest interest) {
+ LOGGER.info("Failed to receive data for interest: " + interest.toUri());
+ response2.countDown();
+ }
+ });
+ b.processEvents();
+ a.processEvents();
+ b.processEvents();
+ a.processEvents();
+
+ response2.await(1, TimeUnit.SECONDS);
+ assertTrue(received.get());
+ }
+}
\ No newline at end of file