Add satisfies() and cleanup() to repository servers
diff --git a/src/main/java/com/intel/jndn/utils/RepositoryServer.java b/src/main/java/com/intel/jndn/utils/RepositoryServer.java
index 0364eb7..88219d8 100644
--- a/src/main/java/com/intel/jndn/utils/RepositoryServer.java
+++ b/src/main/java/com/intel/jndn/utils/RepositoryServer.java
@@ -27,12 +27,15 @@
 public interface RepositoryServer extends Server {
 
   /**
-   * Store a {@link Data} packet in the server's repository until requested. The
-   * task of removing (or retaining) stale packets is not specified here but
-   * left to the implementation.
+   * Store a {@link Data} packet in the server's repository until requested.
    *
    * @param data the {@link Data} packet to store and serve
    * @throws IOException if the underlying server fails to store the packet
    */
   public void serve(Data data) throws IOException;
+  
+  /**
+   * Clean up stale {@link Data} packets from the underlying content store.
+   */
+  public void cleanup();
 }
diff --git a/src/main/java/com/intel/jndn/utils/SegmentedServer.java b/src/main/java/com/intel/jndn/utils/SegmentedServer.java
index c5cfec5..d89f445 100644
--- a/src/main/java/com/intel/jndn/utils/SegmentedServer.java
+++ b/src/main/java/com/intel/jndn/utils/SegmentedServer.java
@@ -58,11 +58,16 @@
       register();
     }
 
-    InputStream stream = new ByteArrayInputStream(data.getContent().getImmutableArray());
-    List<Data> segments = SegmentedServerHelper.segment(data, stream);
-    for (Data segment : segments) {
-      logger.info("Added segment: " + segment.getName().toUri());
-      repository.put(segment);
+    if (data.getContent().size() >= SegmentedServerHelper.DEFAULT_SEGMENT_SIZE) {
+      InputStream stream = new ByteArrayInputStream(data.getContent().getImmutableArray());
+      List<Data> segments = SegmentedServerHelper.segment(data, stream);
+      for (Data segment : segments) {
+        logger.fine("Adding segment: " + segment.getName().toUri());
+        repository.put(segment);
+      }
+    } else {
+      logger.fine("Adding segment: " + data.getName().toUri());
+      repository.put(data);
     }
   }
 
@@ -71,6 +76,8 @@
    */
   @Override
   public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
+    logger.finer("Serving packet for: " + interest.toUri());
+    
     if (interest.getChildSelector() == -1) {
       try {
         interest.getName().get(-1).toSegment();
@@ -85,7 +92,15 @@
       ByteBuffer buffer = data.wireEncode().buf();
       transport.send(buffer);
     } catch (Exception e) {
-      logger.log(Level.SEVERE, "Failed to send data for: " + interest.toUri(), e);
+      logger.log(Level.SEVERE, "Failed to find data satisfying: " + interest.toUri(), e);
     }
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void cleanup() {
+    repository.cleanup();
+  }
 }
diff --git a/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java b/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java
index 1563c24..8f8ed5b 100644
--- a/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java
+++ b/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java
@@ -50,6 +50,7 @@
       if (interest.matchesName(record.data.getName())) {
         if (hasNoChildSelector(interest) && hasAcceptableFreshness(interest, record)) {
           selectedData = record.data;
+          break;
         } else {
           Name.Component component = getNextComponentAfterLastInterestComponent(record.data, interest);
 
@@ -102,19 +103,14 @@
     }
   }
 
+  /**
+   * @param interest the {@link Interest} to check
+   * @return true if the {@link Interest} has no child selector
+   */
   private static boolean hasNoChildSelector(Interest interest) {
     return interest.getChildSelector() < 0;
   }
 
-  @Override
-  public void cleanup() {
-    for (int i = storage.size() - 1; i >= 0; i--) {
-      if (!isFresh(storage.get(i))) {
-        storage.remove(i);
-      }
-    }
-  }
-
   /**
    * Check if a record is fresh.
    *
@@ -144,6 +140,33 @@
   }
 
   /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean satisfies(Interest interest) {
+    for (Record record : storage) {
+      if (interest.matchesName(record.data.getName()) && hasAcceptableFreshness(interest, record)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void cleanup() {
+    for (int i = storage.size() - 1; i >= 0; i--) {
+      if (!isFresh(storage.get(i))) {
+        synchronized (storage) {
+          storage.remove(i);
+        }
+      }
+    }
+  }
+
+  /**
    * Helper data structure
    */
   private class Record {
diff --git a/src/main/java/com/intel/jndn/utils/repository/Repository.java b/src/main/java/com/intel/jndn/utils/repository/Repository.java
index f64c145..ab9a127 100644
--- a/src/main/java/com/intel/jndn/utils/repository/Repository.java
+++ b/src/main/java/com/intel/jndn/utils/repository/Repository.java
@@ -41,6 +41,16 @@
   public Data get(Interest interest) throws DataNotFoundException;
 
   /**
+   * Check if this repository can satisfy the {@link Interest} with a
+   * {@link Data} packet; this should check not only name matching but freshness
+   * and any other selectors.
+   *
+   * @param interest the {@link Interest} to attempt to satisfy
+   * @return true if a {@link Data} exists that satisfies the {@link Interest}
+   */
+  public boolean satisfies(Interest interest);
+
+  /**
    * Remove all stale {@link Data} packets from the repository.
    */
   public void cleanup();
diff --git a/src/test/java/com/intel/jndn/utils/SegmentedServerTest.java b/src/test/java/com/intel/jndn/utils/SegmentedServerTest.java
index a840cd0..b6d271b 100644
--- a/src/test/java/com/intel/jndn/utils/SegmentedServerTest.java
+++ b/src/test/java/com/intel/jndn/utils/SegmentedServerTest.java
@@ -16,6 +16,7 @@
 import com.intel.jndn.mock.MockFace;
 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.util.Blob;
 import static org.junit.Assert.*;
@@ -57,4 +58,40 @@
     assertEquals(in.getName().toUri(), out.getName().toUri());
     assertEquals("1234", out.getContent().toString());
   }
+  
+  @Test(expected = IOException.class)
+  public void testCleanup() throws Exception{
+    Data in = new Data(new Name("/test"));
+    in.getMetaInfo().setFreshnessPeriod(0);
+    instance.serve(in);
+    Thread.sleep(10);
+    
+    Data out = SegmentedClient.getDefault().getSync(face, new Name("/test"));
+    assertNotNull(out);
+    assertEquals(in.getName(), out.getName());
+    
+    instance.cleanup();
+    SegmentedClient.getDefault().getSync(face, new Name("/test"));
+  }
+  
+  @Test
+  public void testServingNoContent() throws IOException{
+    instance.serve(new Data());
+  }
+  
+  @Test
+  public void testWhenDataNameIsLongerThanInterestName() throws Exception{
+    instance.serve(new Data(new Name("/test/prefix/a/b/c/1")));
+    instance.serve(new Data(new Name("/test/prefix/a/b/c/2")));
+    
+    Interest interest = new Interest(new Name("/test/prefix/a/b"))
+            .setChildSelector(Interest.CHILD_SELECTOR_RIGHT).setInterestLifetimeMilliseconds(100);
+    Data out = SegmentedClient.getDefault().getSync(face, interest);
+    
+    assertNotNull(out);
+    assertEquals("/test/prefix/a/b/c/1", out.getName().toUri());
+    // note that this won't be .../c/2 since .../c/1 satisfies both the Interest
+    // name and "c" is the rightmost component (child selectors operate on the
+    // next component after the Interest name only)
+  }
 }
diff --git a/src/test/java/com/intel/jndn/utils/repository/RepositoryTest.java b/src/test/java/com/intel/jndn/utils/repository/RepositoryTest.java
index 36fed91..f39e028 100644
--- a/src/test/java/com/intel/jndn/utils/repository/RepositoryTest.java
+++ b/src/test/java/com/intel/jndn/utils/repository/RepositoryTest.java
@@ -54,6 +54,16 @@
     Data data2 = instance.get(interest2);
     assertEquals("/a/b/c/e", data2.getName().toUri());
   }
+  
+  @Test
+  public void testChildSelectorsOnExactMatch() throws DataNotFoundException{
+    instance.put(buildData("/a/b/c"));
+    instance.put(buildData("/a/b/d"));
+    
+    Interest interest = buildInterest("/a/b/c").setChildSelector(Interest.CHILD_SELECTOR_LEFT);
+    assertTrue(instance.satisfies(interest));
+    assertEquals("/a/b/c", instance.get(interest).getName().toUri());
+  }
 
   @Test(expected = DataNotFoundException.class)
   public void testFailure() throws DataNotFoundException {
@@ -80,4 +90,31 @@
     interest.setMustBeFresh(true);
     instance.get(interest);
   }
+
+  @Test
+  public void testSatisfies() throws InterruptedException {
+    instance.put(RepoHelper.buildAlmostStaleData("/stale/data"));
+    instance.put(RepoHelper.buildFreshData("/fresh/data"));
+
+    Thread.sleep(10);
+
+    assertTrue(instance.satisfies(buildInterest("/fresh/data")));
+    assertFalse(instance.satisfies(buildInterest("/stale/data")));
+    assertFalse(instance.satisfies(buildInterest("/not/found/data")));
+  }
+
+  @Test
+  public void testChildSelectors() throws DataNotFoundException {
+    instance.put(RepoHelper.buildFreshData("/a/a"));
+    instance.put(RepoHelper.buildFreshData("/a/b/c/1"));
+    instance.put(RepoHelper.buildFreshData("/a/b/c/2"));
+    instance.put(RepoHelper.buildFreshData("/a/b/c/3"));
+    
+    assertTrue(instance.satisfies(buildInterest("/a")));
+    
+    Data out = instance.get(buildInterest("/a").setChildSelector(Interest.CHILD_SELECTOR_RIGHT));
+    assertEquals("/a/b/c/1", out.getName().toUri());
+    // you may think this should be /a/b/c/3, but the child selectors only
+    // operate on the first component after the interest name; in this case, "b"
+  }
 }