Major refactoring (simplification and improvements)

The new implementation largely mimics the implementation in ndn-cxx

Change-Id: Ia7128e9cbdb302ebb3d50fa261fdc1dd3fd0116a
diff --git a/src/main/java/com/intel/jndn/mock/FaceExtension.java b/src/main/java/com/intel/jndn/mock/FaceExtension.java
deleted file mode 100644
index b3b9e3b..0000000
--- a/src/main/java/com/intel/jndn/mock/FaceExtension.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * jndn-mock
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.mock;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import net.named_data.jndn.*;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.encoding.WireFormat;
-import net.named_data.jndn.util.Blob;
-
-/**
- * Provides help for extending {@link Face}; this should be an interface but
- * until it is we need a way to know what is the minimal set of methods to
- * override.
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public abstract class FaceExtension extends Face {
-
-	public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout,
-			WireFormat wireFormat) throws IOException {
-		return expressInterest(interest, onData, onTimeout, wireFormat);
-	}
-
-	public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout) throws IOException {
-		return expressInterest(interest, onData, onTimeout, WireFormat.getDefaultWireFormat());
-	}
-
-	public long expressInterest(Interest interest, OnData onData, WireFormat wireFormat) throws IOException {
-		return expressInterest(interest, onData, null, wireFormat);
-	}
-
-	public long expressInterest(Interest interest, OnData onData) throws IOException {
-		return expressInterest(interest, onData, null, WireFormat.getDefaultWireFormat());
-	}
-
-	public long expressInterest(Name name, Interest interestTemplate, OnData onData, OnTimeout onTimeout,
-			WireFormat wireFormat) throws IOException {
-		Interest interest = new Interest(name);
-		if (interestTemplate != null) {
-			interest.setMinSuffixComponents(interestTemplate.getMinSuffixComponents());
-			interest.setMaxSuffixComponents(interestTemplate.getMaxSuffixComponents());
-			interest.setKeyLocator(interestTemplate.getKeyLocator());
-			interest.setExclude(interestTemplate.getExclude());
-			interest.setChildSelector(interestTemplate.getChildSelector());
-			interest.setMustBeFresh(interestTemplate.getMustBeFresh());
-			interest.setInterestLifetimeMilliseconds(
-					interestTemplate.getInterestLifetimeMilliseconds());
-		} else {
-			interest.setInterestLifetimeMilliseconds(4000.0);
-		}
-
-		return expressInterest(interest, onData, onTimeout, wireFormat);
-	}
-
-	public abstract long registerPrefix(Name prefix,
-																			OnInterestCallback onInterest, OnRegisterFailed onRegisterFailed,
-																			OnRegisterSuccess onRegisterSuccess, ForwardingFlags flags,
-																			WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException;
-
-	public abstract void removeRegisteredPrefix(long registeredPrefixId);
-
-	public abstract long setInterestFilter(InterestFilter filter, OnInterestCallback onInterest);
-
-	public long setInterestFilter(Name prefix, OnInterestCallback onInterest) {
-		return setInterestFilter(new InterestFilter(prefix), onInterest);
-	}
-
-	public abstract void unsetInterestFilter(long interestFilterId);
-
-	public abstract void putData(Data data, WireFormat wireFormat) throws IOException;
-
-	public void putData(Data data) throws IOException {
-		putData(data, WireFormat.getDefaultWireFormat());
-	}
-
-	public abstract void send(ByteBuffer encoding) throws IOException;
-
-	public void send(Blob encoding) throws IOException {
-		send(encoding.buf());
-	}
-
-	public abstract void processEvents() throws IOException, EncodingException;
-
-	public abstract boolean isLocal() throws IOException;
-
-	public abstract void shutdown();
-}
diff --git a/src/main/java/com/intel/jndn/mock/MockFace.java b/src/main/java/com/intel/jndn/mock/MockFace.java
index c36b16e..8b78329 100644
--- a/src/main/java/com/intel/jndn/mock/MockFace.java
+++ b/src/main/java/com/intel/jndn/mock/MockFace.java
@@ -1,6 +1,6 @@
 /*
  * jndn-mock
- * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2013-2015 Regents of the University of California.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU Lesser General Public License,
@@ -13,286 +13,325 @@
  */
 package com.intel.jndn.mock;
 
+import net.named_data.jndn.*;
+import net.named_data.jndn.encoding.ElementListener;
+import net.named_data.jndn.encoding.ElementReader;
+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.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import net.named_data.jndn.transport.Transport;
+import net.named_data.jndn.util.Blob;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map.Entry;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.logging.Logger;
 
-import net.named_data.jndn.*;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.encoding.WireFormat;
-import net.named_data.jndn.security.SecurityException;
-
-/**
- * <p>
- * Use the MockTransport to mock sending data over the network. Allows for
- * testing NDN applications while simulating network IO. TODO implement longest
- * prefix match here for comprehensive testing.
- * </p>
- * <p>
- * Usage
- * </p>
- * <pre><code>
- * Face mockFace = new MockFace();
- * mockFace.registerPrefix(...); // as usual
- * mockFace.expressInterest(...); // as usual
- *
- * // also, simply inject a response that will be returned for an expressed interest
- * mockFace.addResponse(interestName, data);
- * </pre></code>
- *
- * @author Andrew Brown <andrew.brown@intel.com>
+/** A client-side face for unit testing
  */
-public class MockFace extends FaceExtension {
+public class MockFace extends Face
+{
+  public interface SignalOnSendInterest {
+    void emit(Interest interest) throws EncodingException, SecurityException;
+  }
+
+  public interface SignalOnSendData {
+    void emit(Data data);
+  }
+
+  /**
+   * Options for MockFace
+   */
+  public static class Options
+  {
+    /** If true, packets sent out of MockFace will be appended to a container
+     */
+    boolean enablePacketLogging = false;
+
+    /**
+     * If true, prefix registration command will be automatically replied with a successful response
+     */
+    boolean enableRegistrationReply = false;
+  }
+
+  final public static Options DEFAULT_OPTIONS = new Options(){{ enablePacketLogging=true; enableRegistrationReply=true; }};
+
+  /**
+   * Create MockFace that logs packets in sentInterests and sentData and emulates NFD prefix registration
+   */
+  public MockFace() throws SecurityException {
+    this(DEFAULT_OPTIONS);
+  }
+
+  /**
+   * Create MockFace with the specified options
+   * <p>
+   * To create Face that does not log packets:
+   * <pre>
+   *   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)
+   * </pre>
+   *
+   * To create Face that just logs packets in sentInterests and sentData:
+   * <pre>
+   *   new MockFace(new Options(){{ enablePacketLogging=true; }});
+   * </pre>
+   */
+  public MockFace(Options options) throws SecurityException {
+    super(new MockFaceTransport(), null);
+    m_transport = (MockFaceTransport)node_.getTransport();
+    m_keychain = MockKeyChain.configure(new Name("/mock/key"));
+    setCommandSigningInfo(m_keychain, m_keychain.getDefaultCertificateName());
+
+    m_transport.onSendBlock = new MockFaceTransport.OnSendBlockSignal() {
+      @Override
+      public void emit(ByteBuffer buffer) throws EncodingException, SecurityException {
+        // @todo Implement NDNLP processing
+
+        if (buffer.get(0) == Tlv.Interest || buffer.get(0) == Tlv.Data) {
+          TlvDecoder decoder = new TlvDecoder(buffer);
+          if (decoder.peekType(Tlv.Interest, buffer.remaining())) {
+            Interest interest = new Interest();
+            interest.wireDecode(buffer, TlvWireFormat.get());
+
+            for (SignalOnSendInterest signal : onSendInterest) {
+              signal.emit(interest);
+            }
+          } else if (decoder.peekType(Tlv.Data, buffer.remaining())) {
+            Data data = new Data();
+            data.wireDecode(buffer, TlvWireFormat.get());
+
+            for (SignalOnSendData signal : onSendData) {
+              signal.emit(data);
+            }
+          }
+        }
+        else {
+          logger.info("Received an unknown packet");
+        }
+      }
+    };
+
+    if (options.enablePacketLogging) {
+      onSendInterest.add(new SignalOnSendInterest() {
+        @Override
+        public void emit(Interest interest) {
+          sentInterests.add(interest);
+        }
+      });
+
+      onSendData.add(new SignalOnSendData() {
+        @Override
+        public void emit(Data data) {
+          sentData.add(data);
+        }
+      });
+    }
+
+    if (options.enableRegistrationReply) {
+      onSendInterest.add(new SignalOnSendInterest() {
+        @Override
+        public void emit(Interest interest) throws EncodingException, SecurityException {
+          final Name localhostRegistration = new Name("/localhost/nfd/rib");
+          if (!interest.getName().getPrefix(3).equals(localhostRegistration)) {
+            return;
+          }
+
+          ControlParameters params = new ControlParameters();
+          params.wireDecode(interest.getName().get(-5).getValue());
+          params.setFaceId(1);
+          params.setOrigin(0);
+
+          if (interest.getName().get(3).toString().equals("register")) {
+            params.setCost(0);
+          }
+
+          // TODO: replace with jNDN ControlResponse encoding when available
+          //       http://redmine.named-data.net/issues/3455
+          TlvEncoder encoder = new TlvEncoder(256);
+          int saveLength = encoder.getLength();
+          encoder.writeBuffer(params.wireEncode().buf());
+          encoder.writeBlobTlv(Tlv.NfdCommand_StatusText, new Blob("OK").buf());
+          encoder.writeNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode, 200);
+          encoder.writeTypeAndLength(Tlv.NfdCommand_ControlResponse, encoder.getLength() - saveLength);
+
+          Data data = new Data();
+          data.setName(interest.getName());
+          data.setContent(new Blob(encoder.getOutput(), false));
+          m_keychain.sign(data);
+
+          receive(data);
+        }
+      });
+    }
+  }
+
+  /**
+   * Mock reception of the Interest packet on the Face (from transport)
+   */
+  public void receive(Interest interest) throws EncodingException {
+    m_transport.receive(interest.wireEncode().buf());
+  }
+
+  /**
+   * Mock reception of the Data packet on the Face (from transport)
+   */
+  public void receive(Data data) throws EncodingException {
+    m_transport.receive(data.wireEncode().buf());
+  }
+
+  public Transport getTransport() {
+    return m_transport;
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Internal transport for MockFace
+   */
+  private static class MockFaceTransport extends Transport {
+    public interface OnSendBlockSignal {
+      void emit(ByteBuffer buffer) throws EncodingException, SecurityException;
+    }
+
+    public void
+    receive(ByteBuffer block) throws EncodingException {
+      synchronized (m_recvBuffer) {
+        m_recvBuffer.add(block.duplicate());
+      }
+    }
+
+    // from Transport
+
+    @Override
+    public boolean isLocal(ConnectionInfo connectionInfo) {
+      return true;
+    }
+
+    @Override
+    public boolean isAsync() {
+      return false;
+    }
+
+    @Override
+    public void connect(Transport.ConnectionInfo connectionInfo,
+                        ElementListener elementListener, Runnable onConnected) {
+      logger.fine("Connecting...");
+      connected = true;
+      elementReader = new ElementReader(elementListener);
+      if (onConnected != null) {
+        onConnected.run();
+      }
+    }
+
+    @Override
+    public void send(ByteBuffer data) throws IOException {
+      logger.fine("Sending " + (data.capacity() - data.position()) + " bytes");
+
+      try {
+        onSendBlock.emit(data);
+      } catch (EncodingException e) {
+        logger.info("TLV decoding error: " + e.toString());
+      } catch (SecurityException e) {
+        logger.info("Signing error: " + e.toString());
+      }
+    }
+
+    @Override
+    public void processEvents() throws IOException, EncodingException {
+      if (!getIsConnected()) {
+        logger.warning("Not connnected...");
+      }
+
+      while (true) {
+        ByteBuffer block = null;
+        synchronized (m_recvBuffer) {
+          if (!m_recvBuffer.isEmpty()) {
+            block = m_recvBuffer.remove(0);
+          }
+        }
+        if (block == null) {
+          break;
+        }
+        elementReader.onReceivedData(block);
+      }
+    }
+
+    @Override
+    public boolean getIsConnected() {
+      return connected;
+    }
+
+    @Override
+    public void close() throws IOException {
+      logger.fine("Closing...");
+      connected = false;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////
+
+    public OnSendBlockSignal onSendBlock;
+
+    //////////////////////////////////////////////////////////////////////////////
+
+    private static final Logger logger = Logger.getLogger(MockFaceTransport.class.getName());
+
+    private boolean connected;
+    private ElementReader elementReader;
+    private final List<ByteBuffer> m_recvBuffer = new LinkedList<ByteBuffer>();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Interests sent out of this MockFace
+   * <p>
+   * Sent Interests are appended to this container if options.enablePacketLogger is true.
+   * User of this class is responsible for cleaning up the container, if necessary.
+   * After .expressInterest, .processEvents must be called before the Interest would show up here.
+   */
+  public List<Interest> sentInterests = new ArrayList<Interest>();
+
+  /**
+   * Data sent out of this MockFace
+   * <p>
+   * Sent Data are appended to this container if options.enablePacketLogger is true.
+   * User of this class is responsible for cleaning up the container, if necessary.
+   * After .put, .processEvents must be called before the Data would show up here.
+   */
+  public List<Data> sentData = new ArrayList<Data>();
+
+  /**
+   * Emits whenever an Interest is sent
+   * <p>
+   * After .expressInterest, .processEvents must be called before this signal would be emitted.
+   */
+  public List<SignalOnSendInterest> onSendInterest = new ArrayList<SignalOnSendInterest>();
+
+  /**
+   * Emits whenever a Data packet is sent
+   * <p>
+   * After .putData, .processEvents must be called before this signal would be emitted.
+   */
+  public List<SignalOnSendData> onSendData = new ArrayList<SignalOnSendData>();
+
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////////////
 
   private static final Logger logger = Logger.getLogger(MockFace.class.getName());
-  private final Node node_;
-  HashMap<String, Data> responseMap = new HashMap<String, Data>();
-  HashMap<Long, MockOnInterestHandler> handlerMap = new HashMap<Long, MockOnInterestHandler>();
-  long lastPendingInterestId = 0;
-  long lastInterestFilterId = 0;
-  long lastRegisteredPrefixId = 0;
-
-  /**
-   * Create a new Face to mock communication over the network; all packets are
-   * maintained in memory
-   */
-  public MockFace() {
-    node_ = new Node(new MockTransport(), null);
-  }
-
-  /**
-   * @return a reference to the current MockTransport
-   */
-  public MockTransport getTransport() {
-    return (MockTransport) node_.getTransport();
-  }
-
-  /**
-   * Add a response Data packet to send immediately when an Interest with a
-   * matching name is received; will continue to respond with the same packet
-   * over multiple requests. This will preempt any registered OnInterest
-   * handlers.
-   *
-   * @param name
-   * @param data
-   */
-  public void addResponse(Name name, Data data) {
-    logger.fine("Added response for: " + name.toUri());
-    responseMap.put(name.toUri(), data);
-  }
-
-  /**
-   * Stop sending a response for the given name.
-   *
-   * @param name
-   */
-  public void removeResponse(Name name) {
-    logger.fine("Removed response for: " + name.toUri());
-    responseMap.remove(name);
-  }
-
-  /**
-   * Handle incoming Interest packets; when an Interest is expressed through
-   * expressInterest(), this will run to determine if: 1) any responses have
-   * been registered or 2) if any OnInterest handlers have been registered. If
-   * one of these two succeeds, this method then re-directs the Interest from
-   * traveling down the network stack and returns data.
-   *
-   * @param interest
-   */
-  protected void handleIncomingRequests(Interest interest) {
-    String interestName = interest.getName().toUri();
-    long registeredPrefixId = findRegisteredHandler(interest);
-    // check if response registered
-    if (responseMap.containsKey(interestName)) {
-      logger.fine("Found response for: " + interestName);
-      Data data = responseMap.get(interestName);
-      ((MockTransport) node_.getTransport()).respondWith(data);
-    } // check if handler registered
-    else if (registeredPrefixId != -1) {
-      logger.fine("Found handler for: " + interestName);
-      MockOnInterestHandler handler = handlerMap.get(findRegisteredHandler(interest));
-      handler.signal(interest, registeredPrefixId);
-    } // log failure
-    else {
-      logger.warning("No response found for interest (aborting): " + interestName);
-    }
-  }
-
-  /**
-   * Find a handler that matches the incoming interest; currently, the only
-   * flags supported are the ChildInherit flags.
-   *
-   * @param interest
-   * @return
-   */
-  protected long findRegisteredHandler(Interest interest) {
-    for (Entry<Long, MockOnInterestHandler> entry : handlerMap.entrySet()) {
-      MockOnInterestHandler handler = entry.getValue();
-      if (handler.flags.getChildInherit() && handler.prefix.match(interest.getName())) {
-        return entry.getKey();
-      }
-      if (handler.prefix.equals(interest.getName())) {
-        return entry.getKey();
-      }
-    }
-    return -1;
-  }
-
-  /**
-   * Helper class for holding references to OnInterest handlers
-   */
-  class MockOnInterestHandler {
-
-    Name prefix;
-    OnInterest onInterest;
-    OnInterestCallback onInterestCallback;
-    ForwardingFlags flags;
-
-    public MockOnInterestHandler(Name prefix, OnInterest onInterest, ForwardingFlags flags) {
-      this.prefix = prefix;
-      this.onInterest = onInterest;
-      this.flags = flags;
-    }
-    
-    public MockOnInterestHandler(Name prefix, OnInterestCallback onInterestCallback, ForwardingFlags flags) {
-      this.prefix = prefix;
-      this.onInterestCallback = onInterestCallback;
-      this.flags = flags;
-    }
-    
-    public void signal(Interest interest, long registeredPrefixId){
-      if(onInterest != null){
-        onInterest.onInterest(prefix, interest, node_.getTransport(), registeredPrefixId);
-      }
-      if(onInterestCallback != null){
-        onInterestCallback.onInterest(prefix, interest, MockFace.this, registeredPrefixId, null);
-      }
-    }
-  }
-
-  /**
-   * Send the Interest through the transport, read the entire response and call
-   * onData(interest, data).
-   *
-   * @param interest The Interest to send. This copies the Interest.
-   * @param onData When a matching data packet is received, this calls
-   * onData.onData(interest, data) where interest is the interest given to
-   * expressInterest and data is the received Data object. NOTE: You must not
-   * change the interest object - if you need to change it then make a copy.
-   * @param onTimeout If the interest times out according to the interest
-   * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
-   * interest given to expressInterest. If onTimeout is null, this does not use
-   * it.
-   * @param wireFormat A WireFormat object used to encode the message.
-   * @return The pending interest ID which can be used with
-   * removePendingInterest.
-   * @throws IOException For I/O error in sending the interest.
-   */
-  @Override
-  public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout,
-          WireFormat wireFormat) throws IOException {
-    long id = lastPendingInterestId++;
-    node_.expressInterest(id, interest, onData, onTimeout, wireFormat, this);
-    handleIncomingRequests(interest);
-    return id;
-  }
-
-  /**
-   * Register prefix with the connected NDN hub and call onInterest when a
-   * matching interest is received. If you have not called
-   * setCommandSigningInfo, this assumes you are connecting to NDNx. If you have
-   * called setCommandSigningInfo, this first sends an NFD registration request,
-   * and if that times out then this sends an NDNx registration request. If you
-   * need to register a prefix with NFD, you must first call
-   * setCommandSigningInfo.
-   *
-   * @param prefix A Name for the prefix to register. This copies the Name.
-   * @param onInterest When an interest is received which matches the name
-   * prefix, this calls onInterest.onInterest(prefix, interest, transport,
-   * registeredPrefixId). NOTE: You must not change the prefix object - if you
-   * need to change it then make a copy.
-   * @param onRegisterFailed If register prefix fails for any reason, this calls
-   * onRegisterFailed.onRegisterFailed(prefix).
-   * @param flags The flags for finer control of which interests are forwarded
-   * to the application.
-   * @param wireFormat A WireFormat object used to encode the message.
-   * @return The lastRegisteredPrefixId prefix ID which can be used with
- removeRegisteredPrefix.
-   * @throws IOException For I/O error in sending the registration request.
-   * @throws SecurityException If signing a command interest for NFD and cannot
-   * find the private key for the certificateName.
-   */
-  @Override
-  public long registerPrefix(Name prefix,
-                             OnInterestCallback onInterest, OnRegisterFailed onRegisterFailed,
-                             OnRegisterSuccess onRegisterSuccess, ForwardingFlags flags,
-                             WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
-    // since we don't send an Interest, ensure the transport is connected
-    if (!getTransport().getIsConnected()) {
-      getTransport().connect(node_.getConnectionInfo(), node_, null);
-    }
-
-    lastRegisteredPrefixId++;
-    handlerMap.put(lastRegisteredPrefixId, new MockOnInterestHandler(prefix, onInterest, flags));
-    return lastRegisteredPrefixId;
-  }
-
-  /**
-   * Process any packets to receive and call callbacks such as onData,
-   * onInterest or onTimeout. This returns immediately if there is no data to
-   * receive. This blocks while calling the callbacks. You should repeatedly
-   * call this from an event loop, with calls to sleep as needed so that the
-   * loop doesn’t use 100% of the CPU. Since processEvents modifies the pending
-   * interest table, your application should make sure that it calls
-   * processEvents in the same thread as expressInterest (which also modifies
-   * the pending interest table). This may throw an exception for reading data
-   * or in the callback for processing the data. If you call this from an main
-   * event loop, you may want to catch and log/disregard all exceptions.
-   */
-  @Override
-  public void processEvents() throws IOException, EncodingException {
-    // Just call Node's processEvents.
-    node_.processEvents();
-  }
-
-  @Override
-  public void removeRegisteredPrefix(long registeredPrefixId) {
-    handlerMap.remove(registeredPrefixId);
-  }
-
-  @Override
-  public long setInterestFilter(InterestFilter filter, OnInterestCallback onInterest) {
-    long id = lastInterestFilterId++;
-    node_.setInterestFilter(id, filter, onInterest, this);
-    return id;
-  }
-
-  @Override
-  public void unsetInterestFilter(long interestFilterId) {
-    node_.unsetInterestFilter(interestFilterId);
-  }
-
-  @Override
-  public void putData(Data data, WireFormat wireFormat) throws IOException {
-    node_.putData(data, wireFormat);
-  }
-
-  @Override
-  public void send(ByteBuffer encoding) throws IOException {
-    node_.send(encoding);
-  }
-
-  @Override
-  public boolean isLocal() throws IOException {
-    return true;
-  }
-
-  @Override
-  public void shutdown() {
-    node_.shutdown();
-  }
-
+  private MockFaceTransport m_transport;
+  private KeyChain m_keychain;
 }
diff --git a/src/main/java/com/intel/jndn/mock/MockKeyChain.java b/src/main/java/com/intel/jndn/mock/MockKeyChain.java
index 2ac92cf..87b82ec 100644
--- a/src/main/java/com/intel/jndn/mock/MockKeyChain.java
+++ b/src/main/java/com/intel/jndn/mock/MockKeyChain.java
@@ -37,7 +37,6 @@
    * @throws net.named_data.jndn.security.SecurityException
    */
   public static KeyChain configure(Name name) throws net.named_data.jndn.security.SecurityException {
-    // access key chain in ~/.ndn; create if necessary 
     PrivateKeyStorage keyStorage = new MemoryPrivateKeyStorage();
     IdentityStorage identityStorage = new MemoryIdentityStorage();
     KeyChain keyChain = new KeyChain(new IdentityManager(identityStorage, keyStorage),
@@ -45,8 +44,7 @@
 
     // create keys, certs if necessary
     if (!identityStorage.doesIdentityExist(name)) {
-      Name keyName = keyChain.createIdentity(name);
-      keyChain.setDefaultKeyForIdentity(keyName, name);
+      keyChain.createIdentityAndCertificate(name);
     }
 
     // set default identity
diff --git a/src/main/java/com/intel/jndn/mock/MockTransport.java b/src/main/java/com/intel/jndn/mock/MockTransport.java
deleted file mode 100644
index c48fa07..0000000
--- a/src/main/java/com/intel/jndn/mock/MockTransport.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * jndn-mock
- * Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 3, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- */
-package com.intel.jndn.mock;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Logger;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Interest;
-import net.named_data.jndn.encoding.ElementListener;
-import net.named_data.jndn.encoding.ElementReader;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.transport.Transport;
-
-/**
- * Mock the transport class Example: ...
- *
- * @author Andrew Brown <andrew.brown@intel.com>
- */
-public class MockTransport extends Transport {
-
-  public final static int BUFFER_CAPACITY = 1024 * 1024; // allocate 1MB buffer
-  private static final Logger logger = Logger.getLogger(MockTransport.class.getName());
-  protected boolean connected;
-  protected ElementReader elementReader;
-  private ByteBuffer buffer = ByteBuffer.allocate(BUFFER_CAPACITY);
-  protected ByteBuffer sentBuffer = ByteBuffer.allocate(BUFFER_CAPACITY);
-  protected List<Data> sentDataPackets = new ArrayList<>();
-  protected List<Interest> sentInterestPackets = new ArrayList<>();
-
-  /**
-   * This transport is always local
-   *
-   * @param connectionInfo
-   * @return
-   */
-  public boolean isLocal(Transport.ConnectionInfo connectionInfo) {
-    return true;
-  }
-
-  /**
-   * Place data in the receive queue; when processEvents() is called, the
-   * calling application will receive these bytes.
-   *
-   * @param response
-   */
-  public void respondWith(ByteBuffer response) {
-    buffer.put(response);
-  }
-
-  /**
-   * Place data in the receive queue; when processEvents() is called, the
-   * calling application will receive this packet.
-   *
-   * @param response
-   */
-  public void respondWith(Data response) {
-    respondWith(response.wireEncode().buf());
-  }
-
-  /**
-   * Place data in the receive queue; when processEvents() is called, the
-   * calling application will receive this packet.
-   *
-   * @param request
-   */
-  public void respondWith(Interest request) {
-    respondWith(request.wireEncode().buf());
-  }
-
-  /**
-   * Inspect the bytes sent using this transport.
-   *
-   * @return
-   */
-  public ByteBuffer getSentBuffer() {
-    return sentBuffer;
-  }
-
-  /**
-   * Inspect the list of data packets sent using this transport; the alternative
-   * is to inspect getSentBuffer().
-   *
-   * @return
-   */
-  public List<Data> getSentDataPackets() {
-    return sentDataPackets;
-  }
-
-  /**
-   * Inspect the list of interest packets sent using this transport; the
-   * alternative is to inspect getSentBuffer().
-   *
-   * @return
-   */
-  public List<Interest> getSentInterestPackets() {
-    return sentInterestPackets;
-  }
-
-  /**
-   * Clear all sent and to-be-received data
-   */
-  public void clear() {
-    buffer = ByteBuffer.allocate(BUFFER_CAPACITY);
-    sentBuffer = ByteBuffer.allocate(BUFFER_CAPACITY);
-    sentDataPackets.clear();
-    sentInterestPackets.clear();
-  }
-
-  /**
-   * Mock the connection startup; calls this processEvents().
-   *
-   * @param connectionInfo
-   * @param elementListener
-   * @throws IOException
-   */
-  @Override
-  public void connect(Transport.ConnectionInfo connectionInfo,
-          ElementListener elementListener, Runnable onConnected) throws IOException {
-    logger.fine("Connecting...");
-    connected = true;
-    elementReader = new ElementReader(elementListener);
-    if (onConnected != null) {
-      onConnected.run();
-    }
-  }
-
-  /**
-   * Mock sending data to the host; access the data as bytes using
-   * getSentBuffer() or as packets with getSentDataPackets().
-   *
-   * @param data The buffer of data to send. This reads from position() to
-   * limit(), but does not change the position.
-   * @throws IOException For I/O error.
-   */
-  @Override
-  public void send(ByteBuffer data) throws IOException {
-    logger.fine("Sending " + (data.capacity() - data.position()) + " bytes");
-
-    // add to sent bytes
-    buffer.put(data);
-    data.flip();
-    sentBuffer.put(data);
-    data.flip();
-
-    // add to sent packets
-    byte first = data.get();
-    if (first == 5) {
-      data.position(0);
-      addSentInterest(data);
-    } else if (first == 6) {
-      data.position(0);
-      addSentData(data);
-    } else {
-      logger.warning("Unknown TLV packet type; cannot parse.");
-    }
-  }
-
-  /**
-   * Helper method to parse Data packets.
-   *
-   * @param data
-   */
-  protected void addSentData(ByteBuffer data) {
-    Data packet = new Data();
-    try {
-      packet.wireDecode(data);
-      sentDataPackets.add(packet);
-    } catch (EncodingException e) {
-      logger.warning("Failed to parse bytes into a data packet");
-    }
-  }
-
-  /**
-   * Helper method to parse Interest packets.
-   *
-   * @param data
-   */
-  protected void addSentInterest(ByteBuffer data) {
-    Interest packet = new Interest();
-    try {
-      packet.wireDecode(data);
-      sentInterestPackets.add(packet);
-    } catch (EncodingException e) {
-      logger.warning("Failed to parse bytes into an interest packet");
-    }
-  }
-
-  /**
-   * Process any data to receive and clear the input buffer; to mock incoming
-   * Data packets, add data to the buffer with respondWith().
-   *
-   * @throws IOException For I/O error.
-   * @throws EncodingException For invalid encoding.
-   */
-  @Override
-  public void processEvents() throws IOException, EncodingException {
-    if (!getIsConnected()) {
-      logger.warning("Not connnected, aborting...");
-      return;
-    }
-
-    // trace data sent
-    logger.finer(String.format("Processing buffer (position: %s, limit: %s, capacity: %s): %s", buffer.position(), buffer.limit(), buffer.capacity(), Arrays.toString(buffer.array())));
-
-    // pass data up to face
-    ByteBuffer temp = copy(buffer);
-    temp.flip();
-
-    // reset buffer
-    buffer = ByteBuffer.allocate(BUFFER_CAPACITY);
-
-    elementReader.onReceivedData(temp);
-  }
-
-  /**
-   * Copy one buffer to a new buffer, preserving the source buffer's position
-   * and limit.
-   *
-   * @param source the source buffer
-   * @return a copied buffer
-   */
-  private ByteBuffer copy(ByteBuffer source) {
-    ByteBuffer dest = ByteBuffer.allocate(source.capacity());
-
-    int saveLimit = source.limit();
-    int savePosition = source.position();
-    source.flip();
-
-    dest.put(source);
-
-    source.limit(saveLimit);
-    source.position(savePosition);
-
-    return dest;
-  }
-
-  /**
-   * Check if the transport is connected.
-   *
-   * @return true if connected.
-   */
-  @Override
-  public boolean getIsConnected() {
-    return connected;
-  }
-
-  /**
-   * Close the connection.
-   *
-   * @throws IOException For I/O error.
-   */
-  @Override
-  public void close() throws IOException {
-    logger.fine("Closing...");
-    connected = false;
-  }
-
-  @Override
-  public boolean isAsync() {
-    return false;
-  }
-}