Merge remote-tracking branch 'teamforge/master'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c7ccd3b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# Created by https://www.gitignore.io
+
+### Maven ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+
+
+### Java ###
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Linux temporary files
+*~
diff --git a/nb-configuration.xml b/nb-configuration.xml
new file mode 100644
index 0000000..38ca257
--- /dev/null
+++ b/nb-configuration.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-shared-configuration>
+    <!--
+This file contains additional configuration written by modules in the NetBeans IDE.
+The configuration is intended to be shared among all the users of project and
+therefore it is assumed to be part of version control checkout.
+Without this configuration present, some functionality in the IDE may be limited or fail altogether.
+-->
+    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
+        <!--
+Properties that influence various parts of the IDE, especially code formatting and the like. 
+You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
+That way multiple projects can share the same settings (useful for formatting rules for example).
+Any value defined here will override the pom.xml file value but is only applicable to the current project.
+-->
+        <org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>2</org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.tab-size>4</org-netbeans-modules-editor-indent.CodeStyle.project.tab-size>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>2</org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs>true</org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width>80</org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width>
+        <org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap>none</org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap>
+        <org-netbeans-modules-editor-indent.text.x-fortran.CodeStyle.project.text-limit-width>132</org-netbeans-modules-editor-indent.text.x-fortran.CodeStyle.project.text-limit-width>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaces-per-tab>2</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaces-per-tab>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.tab-size>4</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.tab-size>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.indent-shift-width>2</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.indent-shift-width>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.expand-tabs>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.expand-tabs>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width>80</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width>
+        <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-line-wrap>none</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-line-wrap>
+    </properties>
+</project-shared-configuration>
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1ac549e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.intel.jndn.mock</groupId>
+  <artifactId>jndn-mock</artifactId>
+  <version>0.9.1</version>
+  <packaging>jar</packaging>
+  <description>Tools for testing NDN Java code without using network IO</description>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <maven.compiler.source>1.7</maven.compiler.source>
+    <maven.compiler.target>1.7</maven.compiler.target>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>net.named_data.jndn</groupId>
+      <artifactId>jndn</artifactId>
+      <version>RELEASE</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <version>2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>2.1</version>
+    </dependency>
+    <!-- Test dependencies -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <repositories>
+    <repository>
+      <id>ubit-artifactory-or.intel.com</id>
+      <name>IT Artifactory (libs)</name>
+      <url>https://ubit-artifactory-or.intel.com/artifactory/libs-release-local</url>
+    </repository>
+  </repositories>
+  <distributionManagement>
+    <repository>
+      <id>ubit-artifactory-or.intel.com</id>
+      <name>ubit-artifactory-or.intel.com-releases</name>
+      <url>https://ubit-artifactory-or.intel.com/artifactory/libs-release-local</url>
+    </repository>
+    <snapshotRepository>
+      <id>ubit-artifactory-or.intel.com</id>
+      <name>ubit-artifactory-or.intel.com-snapshots</name>
+      <url>https://ubit-artifactory-or.intel.com/artifactory/libs-snapshot-local</url>
+    </snapshotRepository>
+  </distributionManagement>
+</project>
\ No newline at end of file
diff --git a/src/main/java/com/intel/jndn/mock/MockFace.java b/src/main/java/com/intel/jndn/mock/MockFace.java
new file mode 100644
index 0000000..8fb1f39
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/MockFace.java
@@ -0,0 +1,572 @@
+/*
+ * File name: MockTransport.java
+ * 
+ * Purpose: Use the MockTransport to mock sending data over the network.
+ * 
+ * © Copyright Intel Corporation. All rights reserved.
+ * Intel Corporation, 2200 Mission College Boulevard,
+ * Santa Clara, CA 95052-8119, USA
+ */
+package com.intel.jndn.mock;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.Node;
+import net.named_data.jndn.OnData;
+import net.named_data.jndn.OnInterest;
+import net.named_data.jndn.OnRegisterFailed;
+import net.named_data.jndn.OnTimeout;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.WireFormat;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class MockFace {
+
+  private static final Logger logger = LogManager.getLogger();
+  private final Node node_;
+  HashMap<String, Data> responseMap = new HashMap<>();
+  HashMap<Long, MockOnInterestHandler> handlerMap = new HashMap<>();
+  long lastRegisteredId = 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);
+  }
+
+  /**
+   * 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.debug("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.debug("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.debug("Found response for: " + interestName);
+      Data data = responseMap.get(interestName);
+      ((MockTransport) node_.getTransport()).respondWith(data);
+    } // check if handler registered
+    else if (registeredPrefixId != -1) {
+      logger.debug("Found handler for: " + interestName);
+      MockOnInterestHandler handler = handlerMap.get(findRegisteredHandler(interest));
+      handler.onInterest.onInterest(handler.prefix, interest, node_.getTransport(), registeredPrefixId);
+    } // log failure
+    else {
+      logger.warn("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;
+    ForwardingFlags flags;
+
+    public MockOnInterestHandler(Name prefix, OnInterest onInterest, ForwardingFlags flags) {
+      this.prefix = prefix;
+      this.onInterest = onInterest;
+      this.flags = flags;
+    }
+  }
+
+  /**
+   * 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.
+   */
+  public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout,
+          WireFormat wireFormat) throws IOException {
+    long id = node_.expressInterest(interest, onData, onTimeout, wireFormat);
+    handleIncomingRequests(interest);
+    return id;
+  }
+
+  /**
+   * Send the Interest through the transport, read the entire response and call
+   * onData(interest, data). This uses the default
+   * WireFormat.getDefaultWireFormat().
+   *
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout) throws IOException {
+    return expressInterest(interest, onData, onTimeout, WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Send the Interest through the transport, read the entire response and call
+   * onData(interest, data). Ignore if the interest times out.
+   *
+   * @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 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.
+   */
+  public long expressInterest(Interest interest, OnData onData, WireFormat wireFormat) throws IOException {
+    return expressInterest(interest, onData, null, wireFormat);
+  }
+
+  /**
+   * Send the Interest through the transport, read the entire response and call
+   * onData(interest, data). Ignore if the interest times out. This uses the
+   * default WireFormat.getDefaultWireFormat().
+   *
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Interest interest, OnData onData) throws IOException {
+    return expressInterest(interest, onData, null, WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data).
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @param interestTemplate If not null, copy interest selectors from the
+   * template. This does not keep a pointer to the Interest object.
+   * @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.
+   */
+  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.setScope(interestTemplate.getScope());
+      interest.setInterestLifetimeMilliseconds(
+              interestTemplate.getInterestLifetimeMilliseconds());
+      // Don't copy the nonce.
+    } else {
+      interest.setInterestLifetimeMilliseconds(4000.0);
+    }
+
+    return expressInterest(interest, onData, onTimeout, wireFormat);
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Use a default interest
+   * lifetime.
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @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.
+   */
+  public long expressInterest(Name name, OnData onData, OnTimeout onTimeout,
+          WireFormat wireFormat) throws IOException {
+    return expressInterest(name, null, onData, onTimeout, wireFormat);
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Ignore if the interest
+   * times out.
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @param interestTemplate If not null, copy interest selectors from the
+   * template. This does not keep a pointer to the Interest object.
+   * @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 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.
+   */
+  public long expressInterest(Name name, Interest interestTemplate, OnData onData,
+          WireFormat wireFormat) throws IOException {
+    return expressInterest(name, interestTemplate, onData, null, wireFormat);
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). This uses the default
+   * WireFormat.getDefaultWireFormat().
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @param interestTemplate If not null, copy interest selectors from the
+   * template. This does not keep a pointer to the Interest object.
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Name name, Interest interestTemplate, OnData onData,
+          OnTimeout onTimeout) throws IOException {
+    return expressInterest(name, interestTemplate, onData, onTimeout,
+            WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Ignore if the interest
+   * times out. This uses the default WireFormat.getDefaultWireFormat().
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @param interestTemplate If not null, copy interest selectors from the
+   * template. This does not keep a pointer to the Interest object.
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Name name, Interest interestTemplate, OnData onData) throws IOException {
+    return expressInterest(name, interestTemplate, onData, null, WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Use a default interest
+   * lifetime. This uses the default WireFormat.getDefaultWireFormat().
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Name name, OnData onData, OnTimeout onTimeout) throws IOException {
+    return expressInterest(name, null, onData, onTimeout, WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Use a default interest
+   * lifetime. Ignore if the interest times out.
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @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 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.
+   */
+  public long expressInterest(Name name, OnData onData, WireFormat wireFormat) throws IOException {
+    return expressInterest(name, null, onData, null, wireFormat);
+  }
+
+  /**
+   * Encode name as an Interest. If interestTemplate is not null, use its
+   * interest selectors. Send the interest through the transport, read the
+   * entire response and call onData(interest, data). Use a default interest
+   * lifetime. Ignore if the interest times out. This uses the default
+   * WireFormat.getDefaultWireFormat().
+   *
+   * @param name A Name for the interest. This copies the Name.
+   * @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.
+   * @return The pending interest ID which can be used with
+   * removePendingInterest.
+   * @throws IOException For I/O error in sending the interest.
+   */
+  public long expressInterest(Name name, OnData onData) throws IOException {
+    return expressInterest(name, null, onData, null, WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Remove the pending interest entry with the pendingInterestId from the
+   * pending interest table. This does not affect another pending interest with
+   * a different pendingInterestId, even if it has the same interest name. If
+   * there is no entry with the pendingInterestId, do nothing.
+   *
+   * @param pendingInterestId The ID returned from expressInterest.
+   */
+  public void removePendingInterest(long pendingInterestId) {
+    node_.removePendingInterest(pendingInterestId);
+  }
+
+  /**
+   * 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 lastRegisteredId 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.
+   */
+  public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
+          ForwardingFlags flags, WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
+    lastRegisteredId++;
+    handlerMap.put(lastRegisteredId, new MockOnInterestHandler(prefix, onInterest, flags));
+    return lastRegisteredId;
+  }
+
+  /**
+   * Register prefix with the connected NDN hub and call onInterest when a
+   * matching interest is received. This uses the default
+   * WireFormat.getDefaultWireFormat().
+   *
+   * @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.
+   * @return The lastRegisteredId prefix ID which can be used with
+   * removeRegisteredPrefix.
+   * @throws IOException For I/O error in sending the registration request.
+   */
+  public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
+          ForwardingFlags flags) throws IOException, net.named_data.jndn.security.SecurityException {
+    return registerPrefix(prefix, onInterest, onRegisterFailed, flags,
+            WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Register prefix with the connected NDN hub and call onInterest when a
+   * matching interest is received. Use default ForwardingFlags.
+   *
+   * @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 wireFormat A WireFormat object used to encode the message.
+   * @return The lastRegisteredId 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.
+   */
+  public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
+          WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
+    return registerPrefix(prefix, onInterest, onRegisterFailed, new ForwardingFlags(), wireFormat);
+  }
+
+  /**
+   * Register prefix with the connected NDN hub and call onInterest when a
+   * matching interest is received. This uses the default
+   * WireFormat.getDefaultWireFormat(). Use default ForwardingFlags.
+   *
+   * @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).
+   * @return The lastRegisteredId 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.
+   */
+  public long registerPrefix(Name prefix, OnInterest onInterest,
+          OnRegisterFailed onRegisterFailed) throws IOException, net.named_data.jndn.security.SecurityException {
+    return registerPrefix(prefix, onInterest, onRegisterFailed, new ForwardingFlags(),
+            WireFormat.getDefaultWireFormat());
+  }
+
+  /**
+   * Remove the lastRegisteredId prefix entry with the registeredPrefixId from
+   * the lastRegisteredId prefix table. This does not affect another
+   * lastRegisteredId prefix with a different registeredPrefixId, even if it has
+   * the same prefix name. If there is no entry with the registeredPrefixId, do
+   * nothing.
+   *
+   * @param registeredPrefixId The ID returned from registerPrefix.
+   */
+  public void removeRegisteredPrefix(long registeredPrefixId) {
+    handlerMap.remove(registeredPrefixId);
+  }
+
+  /**
+   * 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.
+   */
+  public void processEvents() throws IOException, EncodingException {
+    // Just call Node's processEvents.
+    node_.processEvents();
+  }
+
+  /**
+   * Shut down and disconnect this Face.
+   */
+  public void shutdown() {
+    node_.shutdown();
+  }
+}
diff --git a/src/main/java/com/intel/jndn/mock/MockTransport.java b/src/main/java/com/intel/jndn/mock/MockTransport.java
new file mode 100644
index 0000000..0704f94
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/MockTransport.java
@@ -0,0 +1,234 @@
+/*
+ * File name: MockTransport.java
+ * 
+ * Purpose: Provide testing functionality for running NDN unit tests without
+ * connecting to the network or requiring an NFD installed.
+ * 
+ * © Copyright Intel Corporation. All rights reserved.
+ * Intel Corporation, 2200 Mission College Boulevard,
+ * Santa Clara, CA 95052-8119, USA
+ */
+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 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;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Mock the transport class Example: ...
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class MockTransport extends Transport {
+
+  public final static int BUFFER_CAPACITY = 2000;
+  private static final Logger logger = LogManager.getLogger();
+  protected boolean connected;
+  protected ElementReader elementReader;
+  protected 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<>();
+
+  /**
+   * 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) throws IOException {
+    logger.debug("Connecting...");
+    connected = true;
+    elementReader = new ElementReader(elementListener);
+  }
+
+  /**
+   * 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.debug("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.warn("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.warn("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.warn("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.warn("Not connnected, aborting...");
+      return;
+    }
+
+    // trace data sent
+    logger.trace(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
+    buffer.limit(buffer.position());
+    buffer.position(0);
+    elementReader.onReceivedData(buffer);
+
+    // reset buffer
+    buffer = ByteBuffer.allocate(BUFFER_CAPACITY);
+  }
+
+  /**
+   * 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.debug("Closing...");
+    connected = false;
+  }
+}
diff --git a/src/main/resources/log4j2-test.xml b/src/main/resources/log4j2-test.xml
new file mode 100644
index 0000000..602b5ab
--- /dev/null
+++ b/src/main/resources/log4j2-test.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+    </Console>
+  </Appenders>
+  <Loggers>
+    <Root level="info">
+      <AppenderRef ref="Console"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..0dac33a
--- /dev/null
+++ b/src/main/resources/log4j2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+    </Console>
+  </Appenders>
+  <Loggers>
+    <Root level="warn">
+      <AppenderRef ref="Console"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/src/test/java/com/intel/jndn/mock/MockFaceTest.java b/src/test/java/com/intel/jndn/mock/MockFaceTest.java
new file mode 100644
index 0000000..66db3a2
--- /dev/null
+++ b/src/test/java/com/intel/jndn/mock/MockFaceTest.java
@@ -0,0 +1,133 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.intel.jndn.mock;
+
+import java.io.IOException;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnData;
+import net.named_data.jndn.OnInterest;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.transport.Transport;
+import net.named_data.jndn.util.Blob;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class MockFaceTest {
+
+  /**
+   * Setup logging
+   */
+  private static final Logger logger = LogManager.getLogger();
+
+  /**
+   * Test setting responses for specific names
+   * @throws java.io.IOException
+   * @throws net.named_data.jndn.encoding.EncodingException
+   */
+  @Test
+  public void testWithResponses() throws IOException, EncodingException {
+    MockFace face = new MockFace();
+
+    // add response
+    Data response = new Data(new Name("/test/with/responses"));
+    response.setContent(new Blob("..."));
+    face.addResponse(new Name("/test/with/responses"), response);
+
+    // make request
+    final Counter count = new Counter();
+    logger.info("Express interest: /test/with/responses");
+    face.expressInterest(new Interest(new Name("/test/with/responses")), new OnData() {
+      @Override
+      public void onData(Interest interest, Data data) {
+        count.inc();
+        logger.debug("Received data");
+        assertEquals(data.getContent().buf(), new Blob("...").buf());
+      }
+    });
+
+    // process face until a response is received
+    int allowedLoops = 100;
+    while (count.get() == 0 && allowedLoops > 0) {
+      allowedLoops--;
+      face.processEvents();
+    }
+    assertEquals(1, count.get());
+  }
+
+  /**
+   * Test serving data dynamically with OnInterest handlers
+   * 
+   * @throws net.named_data.jndn.encoding.EncodingException
+   * @throws java.io.IOException
+   * @throws net.named_data.jndn.security.SecurityException
+   */
+  @Test
+  public void testWithHandlers() throws EncodingException, IOException, net.named_data.jndn.security.SecurityException {
+    MockFace face = new MockFace();
+
+    // add interest handler
+    logger.info("Register prefix: /test/with/responses");
+    face.registerPrefix(new Name("/test/with/handlers"), new OnInterest() {
+      @Override
+      public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
+        logger.debug("Received interest, responding: " + interest.getName().toUri());
+        Data response = new Data(new Name("/test/with/handlers"));
+        response.setContent(new Blob("..."));
+        try {
+          transport.send(response.wireEncode().buf());
+        } catch (IOException e) {
+          fail("Failed to send encoded data packet.");
+        }
+      }
+    }, null);
+
+    // make request
+    final Counter count = new Counter();
+    logger.info("Express interest: /test/with/responses");
+    face.expressInterest(new Interest(new Name("/test/with/handlers")), new OnData() {
+      @Override
+      public void onData(Interest interest, Data data) {
+        count.inc();
+        logger.debug("Received data");
+        assertEquals(data.getContent().buf(), new Blob("...").buf());
+      }
+    });
+
+    // process faces until a response is received
+    int allowedLoops = 100;
+    while (count.get() == 0 && allowedLoops > 0) {
+      allowedLoops--;
+      face.processEvents();
+    }
+    assertEquals(1, count.get());
+  }
+  
+  // TODO add childInherit test
+
+  /**
+   * Count reference
+   */
+  class Counter {
+
+    int count = 0;
+
+    public void inc() {
+      count++;
+    }
+
+    public int get() {
+      return count;
+    }
+  }
+}
diff --git a/src/test/java/com/intel/jndn/mock/MockTransportTest.java b/src/test/java/com/intel/jndn/mock/MockTransportTest.java
new file mode 100644
index 0000000..44ad258
--- /dev/null
+++ b/src/test/java/com/intel/jndn/mock/MockTransportTest.java
@@ -0,0 +1,159 @@
+/*
+ * File name: MockTransportTest.java
+ * 
+ * Purpose: Test the MockTransport
+ * 
+ * © Copyright Intel Corporation. All rights reserved.
+ * Intel Corporation, 2200 Mission College Boulevard,
+ * Santa Clara, CA 95052-8119, USA
+ */
+package com.intel.jndn.mock;
+
+import java.io.IOException;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnData;
+import net.named_data.jndn.OnTimeout;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.util.Blob;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Mock the transport class TODO add face.registerPrefix() example
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class MockTransportTest {
+
+  /**
+   * Setup logging
+   */
+  private static final Logger logger = LogManager.getLogger();
+
+  /**
+   * Test sending a Data packet.
+   *
+   * @throws java.io.IOException
+   * @throws net.named_data.jndn.encoding.EncodingException
+   */
+  @Test
+  public void testSendData() throws IOException, EncodingException {
+    MockTransport transport = new MockTransport();
+    Face face = new Face(transport, null);
+
+    // setup return data
+    Data response = new Data(new Name("/a/b/c"));
+    response.setContent(new Blob("..."));
+    transport.respondWith(response);
+
+    // express interest on the face
+    final Counter count = new Counter();
+    face.expressInterest(new Interest(new Name("/a/b/c")), new OnData() {
+      @Override
+      public void onData(Interest interest, Data data) {
+        count.inc();
+        logger.debug("Received data");
+        assertEquals(data.getContent().buf(), new Blob("...").buf());
+      }
+    });
+
+    // process the face until one response
+    while (count.get() == 0) {
+      face.processEvents();
+    }
+
+    // check for sent packets
+    assertEquals(0, transport.getSentDataPackets().size());
+    assertEquals(1, transport.getSentInterestPackets().size());
+  }
+
+  /**
+   * Test sending multiple Data packets.
+   *
+   * @throws java.io.IOException
+   * @throws net.named_data.jndn.encoding.EncodingException
+   */
+  @Test
+  public void testSendMultipleData() throws IOException, EncodingException {
+    MockTransport transport = new MockTransport();
+    Face face = new Face(transport, null);
+
+    // setup return data
+    Data response1 = new Data(new Name("/a/b/c/1"));
+    response1.setContent(new Blob("..."));
+    transport.respondWith(response1);
+    Data response2 = new Data(new Name("/a/b/c/2"));
+    response2.setContent(new Blob("..."));
+    transport.respondWith(response2);
+
+    // express interest on the face
+    final Counter count = new Counter();
+    face.expressInterest(new Interest(new Name("/a/b/c/1")), new OnData() {
+      @Override
+      public void onData(Interest interest, Data data) {
+        count.inc();
+        logger.debug("Received data");
+        assertEquals(data.getContent().buf(), new Blob("...").buf());
+      }
+    });
+
+    // process the face until one response received
+    while (count.get() == 0) {
+      face.processEvents();
+    }
+
+    // check for sent packets
+    assertEquals(0, transport.getSentDataPackets().size());
+    assertEquals(1, transport.getSentInterestPackets().size());
+
+    // express interest again, but this time it should time out because there 
+    // is no data left on the wire; the first processEvents() has already 
+    // picked it up
+    final Counter count2 = new Counter();
+    Interest failingInterest = new Interest(new Name("/a/b/c/2"));
+    failingInterest.setInterestLifetimeMilliseconds(50);
+    face.expressInterest(failingInterest, new OnData() {
+      @Override
+      public void onData(Interest interest, Data data) {
+        count2.inc();
+        fail("Should not return data; data should already be cleared");
+      }
+    }, new OnTimeout() {
+      @Override
+      public void onTimeout(Interest interest) {
+        count2.inc();
+        assertTrue(true);
+      }
+    });
+
+    // process the face until timeout
+    while (count2.get() == 0) {
+      face.processEvents();
+    }
+
+    // check for sent packets
+    assertEquals(0, transport.getSentDataPackets().size());
+    assertEquals(2, transport.getSentInterestPackets().size());
+  }
+
+  /**
+   * Count reference
+   */
+  class Counter {
+
+    int count = 0;
+
+    public void inc() {
+      count++;
+    }
+
+    public int get() {
+      return count;
+    }
+  }
+}