model+ndn.cxx+test: Finishing implementation of ApiFace (now timeouts are working as well)

Also, adding a basic test case to test ApiFace

Refs #1005 (http://redmine.named-data.net/)
diff --git a/bindings/modulegen__gcc_ILP32.py b/bindings/modulegen__gcc_ILP32.py
index 8d51fd8..4255339 100644
--- a/bindings/modulegen__gcc_ILP32.py
+++ b/bindings/modulegen__gcc_ILP32.py
@@ -376,6 +376,8 @@
     module.add_class('StackHelper')
     ## ndn-header-helper.h (module 'ndnSIM'): ns3::ndn::UnknownHeaderException [class]
     module.add_class('UnknownHeaderException')
+    ## ndn-api-face.h (module 'ndnSIM'): ns3::ndn::ApiFace [class]
+    module.add_class('ApiFace', parent=root_module['ns3::ndn::Face'])
     ## ndn-app-face.h (module 'ndnSIM'): ns3::ndn::AppFace [class]
     module.add_class('AppFace', parent=root_module['ns3::ndn::Face'])
     module.add_container('std::vector< ns3::Ptr< ns3::ndn::Face > >', 'ns3::Ptr< ns3::ndn::Face >', container_type='vector')
@@ -618,6 +620,7 @@
     register_Ns3NdnRttHistory_methods(root_module, root_module['ns3::ndn::RttHistory'])
     register_Ns3NdnStackHelper_methods(root_module, root_module['ns3::ndn::StackHelper'])
     register_Ns3NdnUnknownHeaderException_methods(root_module, root_module['ns3::ndn::UnknownHeaderException'])
+    register_Ns3NdnApiFace_methods(root_module, root_module['ns3::ndn::ApiFace'])
     register_Ns3NdnAppFace_methods(root_module, root_module['ns3::ndn::AppFace'])
     register_Ns3NdnCsEntry_methods(root_module, root_module['ns3::ndn::cs::Entry'])
     register_Ns3NdnFibEntry_methods(root_module, root_module['ns3::ndn::fib::Entry'])
@@ -5634,6 +5637,10 @@
     cls.add_constructor([param('std::string const &', 'prefix')])
     ## ndn-name.h (module 'ndnSIM'): ns3::ndn::Name::Name(char const * prefix) [constructor]
     cls.add_constructor([param('char const *', 'prefix')])
+    ## ndn-name.h (module 'ndnSIM'): ns3::ndn::Name & ns3::ndn::Name::Append(ns3::ndn::Name const & otherName) [member function]
+    cls.add_method('Append', 
+                   'ns3::ndn::Name &', 
+                   [param('ns3::ndn::Name const &', 'otherName')])
     ## ndn-name.h (module 'ndnSIM'): std::list<std::string, std::allocator<std::string> > const & ns3::ndn::Name::GetComponents() const [member function]
     cls.add_method('GetComponents', 
                    'std::list< std::string > const &', 
@@ -6037,6 +6044,46 @@
     cls.add_constructor([param('ns3::ndn::UnknownHeaderException const &', 'arg0')])
     return
 
+def register_Ns3NdnApiFace_methods(root_module, cls):
+    ## ndn-api-face.h (module 'ndnSIM'): ns3::ndn::ApiFace::ApiFace(ns3::Ptr<ns3::Node> node) [constructor]
+    cls.add_constructor([param('ns3::Ptr< ns3::Node >', 'node')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::Shutdown() [member function]
+    cls.add_method('Shutdown', 
+                   'void', 
+                   [])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::ExpressInterest(ns3::Ptr<ns3::ndn::Interest> interest, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Interest>,ns3::Ptr<const ns3::ndn::ContentObject>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onData, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Interest>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onTimeout) [member function]
+    cls.add_method('ExpressInterest', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Interest >', 'interest'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Interest const >, ns3::Ptr< ns3::ndn::ContentObject const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onData'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Interest const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onTimeout')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::SetInterestFilter(ns3::Ptr<ns3::ndn::Name const> prefix, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Name>,ns3::Ptr<const ns3::ndn::Interest>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onInterest) [member function]
+    cls.add_method('SetInterestFilter', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Name const >', 'prefix'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Name const >, ns3::Ptr< ns3::ndn::Interest const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onInterest')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::ClearInterestFilter(ns3::Ptr<ns3::ndn::Name const> prefix) [member function]
+    cls.add_method('ClearInterestFilter', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Name const >', 'prefix')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::Put(ns3::Ptr<ns3::ndn::ContentObject> data) [member function]
+    cls.add_method('Put', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::ContentObject >', 'data')])
+    ## ndn-api-face.h (module 'ndnSIM'): bool ns3::ndn::ApiFace::SendInterest(ns3::Ptr<ns3::ndn::Interest const> interest) [member function]
+    cls.add_method('SendInterest', 
+                   'bool', 
+                   [param('ns3::Ptr< ns3::ndn::Interest const >', 'interest')], 
+                   is_virtual=True)
+    ## ndn-api-face.h (module 'ndnSIM'): bool ns3::ndn::ApiFace::SendData(ns3::Ptr<ns3::ndn::ContentObject const> data) [member function]
+    cls.add_method('SendData', 
+                   'bool', 
+                   [param('ns3::Ptr< ns3::ndn::ContentObject const >', 'data')], 
+                   is_virtual=True)
+    ## ndn-api-face.h (module 'ndnSIM'): std::ostream & ns3::ndn::ApiFace::Print(std::ostream & os) const [member function]
+    cls.add_method('Print', 
+                   'std::ostream &', 
+                   [param('std::ostream &', 'os')], 
+                   is_const=True, is_virtual=True)
+    return
+
 def register_Ns3NdnAppFace_methods(root_module, cls):
     ## ndn-app-face.h (module 'ndnSIM'): static ns3::TypeId ns3::ndn::AppFace::GetTypeId() [member function]
     cls.add_method('GetTypeId', 
diff --git a/bindings/modulegen__gcc_LP64.py b/bindings/modulegen__gcc_LP64.py
index 8d51fd8..4255339 100644
--- a/bindings/modulegen__gcc_LP64.py
+++ b/bindings/modulegen__gcc_LP64.py
@@ -376,6 +376,8 @@
     module.add_class('StackHelper')
     ## ndn-header-helper.h (module 'ndnSIM'): ns3::ndn::UnknownHeaderException [class]
     module.add_class('UnknownHeaderException')
+    ## ndn-api-face.h (module 'ndnSIM'): ns3::ndn::ApiFace [class]
+    module.add_class('ApiFace', parent=root_module['ns3::ndn::Face'])
     ## ndn-app-face.h (module 'ndnSIM'): ns3::ndn::AppFace [class]
     module.add_class('AppFace', parent=root_module['ns3::ndn::Face'])
     module.add_container('std::vector< ns3::Ptr< ns3::ndn::Face > >', 'ns3::Ptr< ns3::ndn::Face >', container_type='vector')
@@ -618,6 +620,7 @@
     register_Ns3NdnRttHistory_methods(root_module, root_module['ns3::ndn::RttHistory'])
     register_Ns3NdnStackHelper_methods(root_module, root_module['ns3::ndn::StackHelper'])
     register_Ns3NdnUnknownHeaderException_methods(root_module, root_module['ns3::ndn::UnknownHeaderException'])
+    register_Ns3NdnApiFace_methods(root_module, root_module['ns3::ndn::ApiFace'])
     register_Ns3NdnAppFace_methods(root_module, root_module['ns3::ndn::AppFace'])
     register_Ns3NdnCsEntry_methods(root_module, root_module['ns3::ndn::cs::Entry'])
     register_Ns3NdnFibEntry_methods(root_module, root_module['ns3::ndn::fib::Entry'])
@@ -5634,6 +5637,10 @@
     cls.add_constructor([param('std::string const &', 'prefix')])
     ## ndn-name.h (module 'ndnSIM'): ns3::ndn::Name::Name(char const * prefix) [constructor]
     cls.add_constructor([param('char const *', 'prefix')])
+    ## ndn-name.h (module 'ndnSIM'): ns3::ndn::Name & ns3::ndn::Name::Append(ns3::ndn::Name const & otherName) [member function]
+    cls.add_method('Append', 
+                   'ns3::ndn::Name &', 
+                   [param('ns3::ndn::Name const &', 'otherName')])
     ## ndn-name.h (module 'ndnSIM'): std::list<std::string, std::allocator<std::string> > const & ns3::ndn::Name::GetComponents() const [member function]
     cls.add_method('GetComponents', 
                    'std::list< std::string > const &', 
@@ -6037,6 +6044,46 @@
     cls.add_constructor([param('ns3::ndn::UnknownHeaderException const &', 'arg0')])
     return
 
+def register_Ns3NdnApiFace_methods(root_module, cls):
+    ## ndn-api-face.h (module 'ndnSIM'): ns3::ndn::ApiFace::ApiFace(ns3::Ptr<ns3::Node> node) [constructor]
+    cls.add_constructor([param('ns3::Ptr< ns3::Node >', 'node')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::Shutdown() [member function]
+    cls.add_method('Shutdown', 
+                   'void', 
+                   [])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::ExpressInterest(ns3::Ptr<ns3::ndn::Interest> interest, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Interest>,ns3::Ptr<const ns3::ndn::ContentObject>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onData, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Interest>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onTimeout) [member function]
+    cls.add_method('ExpressInterest', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Interest >', 'interest'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Interest const >, ns3::Ptr< ns3::ndn::ContentObject const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onData'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Interest const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onTimeout')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::SetInterestFilter(ns3::Ptr<ns3::ndn::Name const> prefix, ns3::Callback<void,ns3::Ptr<const ns3::ndn::Name>,ns3::Ptr<const ns3::ndn::Interest>,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty,ns3::empty> onInterest) [member function]
+    cls.add_method('SetInterestFilter', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Name const >', 'prefix'), param('ns3::Callback< void, ns3::Ptr< ns3::ndn::Name const >, ns3::Ptr< ns3::ndn::Interest const >, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'onInterest')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::ClearInterestFilter(ns3::Ptr<ns3::ndn::Name const> prefix) [member function]
+    cls.add_method('ClearInterestFilter', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::Name const >', 'prefix')])
+    ## ndn-api-face.h (module 'ndnSIM'): void ns3::ndn::ApiFace::Put(ns3::Ptr<ns3::ndn::ContentObject> data) [member function]
+    cls.add_method('Put', 
+                   'void', 
+                   [param('ns3::Ptr< ns3::ndn::ContentObject >', 'data')])
+    ## ndn-api-face.h (module 'ndnSIM'): bool ns3::ndn::ApiFace::SendInterest(ns3::Ptr<ns3::ndn::Interest const> interest) [member function]
+    cls.add_method('SendInterest', 
+                   'bool', 
+                   [param('ns3::Ptr< ns3::ndn::Interest const >', 'interest')], 
+                   is_virtual=True)
+    ## ndn-api-face.h (module 'ndnSIM'): bool ns3::ndn::ApiFace::SendData(ns3::Ptr<ns3::ndn::ContentObject const> data) [member function]
+    cls.add_method('SendData', 
+                   'bool', 
+                   [param('ns3::Ptr< ns3::ndn::ContentObject const >', 'data')], 
+                   is_virtual=True)
+    ## ndn-api-face.h (module 'ndnSIM'): std::ostream & ns3::ndn::ApiFace::Print(std::ostream & os) const [member function]
+    cls.add_method('Print', 
+                   'std::ostream &', 
+                   [param('std::ostream &', 'os')], 
+                   is_const=True, is_virtual=True)
+    return
+
 def register_Ns3NdnAppFace_methods(root_module, cls):
     ## ndn-app-face.h (module 'ndnSIM'): static ns3::TypeId ns3::ndn::AppFace::GetTypeId() [member function]
     cls.add_method('GetTypeId', 
diff --git a/examples/custom-apps/ndn-api-app.cc b/examples/custom-apps/ndn-api-app.cc
index 886a0b8..3346b52 100644
--- a/examples/custom-apps/ndn-api-app.cc
+++ b/examples/custom-apps/ndn-api-app.cc
@@ -77,7 +77,7 @@
 void
 ApiApp::StartApplication ()
 {
-  m_face = Create<ApiFace> (GetNode ());
+  m_face = CreateObject<ApiFace> (GetNode ());
   
   Simulator::Schedule (Seconds (1), &::ns3::ndn::ApiApp::RequestData, this);
 }
@@ -85,6 +85,9 @@
 void
 ApiApp::StopApplication ()
 {
+  NS_LOG_FUNCTION (this);
+  m_face->Shutdown ();
+  m_face = 0;
 }
 
 } // namespace ndn
diff --git a/examples/ndn-simple-api.cc b/examples/ndn-simple-api.cc
index 587ca55..5fb4dd9 100644
--- a/examples/ndn-simple-api.cc
+++ b/examples/ndn-simple-api.cc
@@ -76,7 +76,8 @@
   // Consumer
   ndn::AppHelper consumerHelper ("ns3::ndn::ApiApp");
   consumerHelper.SetPrefix ("/prefix");
-  consumerHelper.Install (nodes.Get (0)); // first node
+  ApplicationContainer app = consumerHelper.Install (nodes.Get (0)); // first node
+  app.Stop (Seconds (10.0));
 
   // Producer
   ndn::AppHelper producerHelper ("ns3::ndn::Producer");
diff --git a/model/ndn-l3-protocol.cc b/model/ndn-l3-protocol.cc
index f0c660d..e89c9ea 100644
--- a/model/ndn-l3-protocol.cc
+++ b/model/ndn-l3-protocol.cc
@@ -108,10 +108,10 @@
 {
   NS_LOG_FUNCTION (this);
 
-  for (FaceList::iterator i = m_faces.begin (); i != m_faces.end (); ++i)
-    {
-      *i = 0;
-    }
+  // for (FaceList::iterator i = m_faces.begin (); i != m_faces.end (); ++i)
+  //   {
+  //     *i = 0;
+  //   }
   m_faces.clear ();
   m_node = 0;
 
@@ -142,6 +142,7 @@
 void
 L3Protocol::RemoveFace (Ptr<Face> face)
 {
+  NS_LOG_FUNCTION (this << boost::cref (*face));
   // ask face to register in lower-layer stack
   face->UnRegisterProtocolHandlers ();
   Ptr<Pit> pit = GetObject<Pit> ();
@@ -166,7 +167,10 @@
     }
 
   FaceList::iterator face_it = find (m_faces.begin(), m_faces.end(), face);
-  NS_ASSERT_MSG (face_it != m_faces.end (), "Attempt to remove face that doesn't exist");
+  if (face_it == m_faces.end ())
+    {
+      return;
+    }
   m_faces.erase (face_it);
 
   GetObject<Fib> ()->RemoveFromAll (face);
diff --git a/ndn.cxx/detail/filter-entry.h b/ndn.cxx/detail/filter-entry.h
deleted file mode 100644
index 97b9a14..0000000
--- a/ndn.cxx/detail/filter-entry.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2013, Regents of the University of California
- *                     Alexander Afanasyev
- *
- * GNU v3.0 license, See the LICENSE file for more information
- *
- * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
- */
-
-#ifndef NDN_NDNCXX_DETAIL_FILTER_ENTRY_H
-#define NDN_NDNCXX_DETAIL_FILTER_ENTRY_H
-
-#include <ns3/ndnSIM/utils/trie/trie-with-policy.h>
-#include <ns3/ndnSIM/utils/trie/counting-policy.h>
-
-namespace ns3 {
-namespace ndn {
-
-template<class Callback, class Payload>
-struct FilterEntry : public ns3::SimpleRefCount< FilterEntry<Callback, Payload> >
-{
-public:
-  FilterEntry (Ptr<const Payload> payload)
-    : m_payload (payload)
-  { }
-  
-  void
-  AddCallback (Callback callback)
-  { 
-    m_callback = callback;
-  }
-
-  void
-  ClearCallback ()
-  {
-    m_callback = Callback ();
-  }
-
-  Ptr<const Payload>
-  GetPayload () const
-  {
-    return m_payload;
-  }
-  
-public:
-  Callback m_callback;
-  Ptr<const Payload> m_payload;
-};
-
-
-template<class Callback, class Payload>
-struct FilterEntryContainer :
-    public ns3::ndn::ndnSIM::trie_with_policy<ns3::ndn::Name,
-                                              ns3::ndn::ndnSIM::smart_pointer_payload_traits< FilterEntry<Callback, Payload> >,
-                                              ns3::ndn::ndnSIM::counting_policy_traits>
-{
-};
-
-} // ndn
-} // ns3
-
-#endif // NDN_NDNCXX_DETAIL_FILTER_ENTRY_H
diff --git a/ndn.cxx/detail/pending-interests-container.h b/ndn.cxx/detail/pending-interests-container.h
new file mode 100644
index 0000000..4d96ed4
--- /dev/null
+++ b/ndn.cxx/detail/pending-interests-container.h
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Alexander Afanasyev
+ *
+ * GNU v3.0 license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_NDNCXX_DETAIL_PENDING_INTEREST_CONTAINER_H
+#define NDN_NDNCXX_DETAIL_PENDING_INTEREST_CONTAINER_H
+
+#include <ns3/ndnSIM/utils/trie/trie-with-policy.h>
+#include "timeouts-policy.h"
+
+namespace ns3 {
+namespace ndn {
+namespace detail {
+
+struct PendingInterestEntry : public SimpleRefCount< PendingInterestEntry >
+{
+public:
+  PendingInterestEntry (Ptr<const Interest> interest)
+    : m_interest (interest)
+  { }
+  
+  void
+  AddCallbacks (ApiFace::DataCallback onData, ApiFace::TimeoutCallback onTimeout)
+  { 
+    m_dataCallback = onData;
+    m_timeoutCallback = onTimeout;
+  }
+
+  void
+  ClearCallbacks ()
+  {
+    m_dataCallback = ApiFace::DataCallback ();
+    m_timeoutCallback = ApiFace::TimeoutCallback ();
+  }
+
+  Ptr<const Interest>
+  GetInterest () const
+  {
+    return m_interest;
+  }
+  
+public:
+  ApiFace::DataCallback m_dataCallback;
+  ApiFace::TimeoutCallback m_timeoutCallback;
+  Ptr<const Interest> m_interest;
+};
+
+
+struct PendingInterestContainer :
+    public ndnSIM::trie_with_policy<Name,
+                                    ndnSIM::smart_pointer_payload_traits< PendingInterestEntry >,
+                                    timeouts_policy_traits>
+{
+};
+
+} // detail
+} // ndn
+} // ns3
+
+#endif // NDN_NDNCXX_DETAIL_PENDING_INTEREST_CONTAINER_H
diff --git a/ndn.cxx/detail/registered-prefix-container.h b/ndn.cxx/detail/registered-prefix-container.h
new file mode 100644
index 0000000..2e6c151
--- /dev/null
+++ b/ndn.cxx/detail/registered-prefix-container.h
@@ -0,0 +1,63 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Alexander Afanasyev
+ *
+ * GNU v3.0 license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_NDNCXX_DETAIL_REGISTERED_PREFIX_CONTAINER_H
+#define NDN_NDNCXX_DETAIL_REGISTERED_PREFIX_CONTAINER_H
+
+#include <ns3/ndnSIM/utils/trie/trie-with-policy.h>
+#include <ns3/ndnSIM/utils/trie/counting-policy.h>
+
+namespace ns3 {
+namespace ndn {
+namespace detail {
+
+struct RegisteredPrefixEntry : public SimpleRefCount< RegisteredPrefixEntry >
+{
+public:
+  RegisteredPrefixEntry (Ptr<const Name> prefix)
+    : m_prefix (prefix)
+  { }
+  
+  void
+  AddCallback (ApiFace::InterestCallback callback)
+  { 
+    m_callback = callback;
+  }
+
+  void
+  ClearCallback ()
+  {
+    m_callback = ApiFace::InterestCallback ();
+  }
+
+  Ptr<const Name>
+  GetPrefix () const
+  {
+    return m_prefix;
+  }
+  
+public:
+  ApiFace::InterestCallback m_callback;
+  Ptr<const Name> m_prefix;
+};
+
+
+struct RegisteredPrefixContainer :
+    public ndnSIM::trie_with_policy<Name,
+                                    ndnSIM::smart_pointer_payload_traits< RegisteredPrefixEntry >,
+                                    ndnSIM::counting_policy_traits>
+{
+};
+
+} // detail
+} // ndn
+} // ns3
+
+#endif // NDN_NDNCXX_DETAIL_REGISTERED_PREFIX_CONTAINER_H
diff --git a/ndn.cxx/detail/timeouts-policy.h b/ndn.cxx/detail/timeouts-policy.h
new file mode 100644
index 0000000..7398d44
--- /dev/null
+++ b/ndn.cxx/detail/timeouts-policy.h
@@ -0,0 +1,167 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Alexander Afanasyev
+ *
+ * GNU v3.0 license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_NDNCXX_DETAIL_TIMEOUTS_POLICY_H_
+#define NDN_NDNCXX_DETAIL_TIMEOUTS_POLICY_H_
+
+#include <boost/intrusive/options.hpp>
+#include <boost/intrusive/list.hpp>
+
+#include <ns3/nstime.h>
+#include <ns3/simulator.h>
+
+namespace ns3 {
+namespace ndn {
+namespace detail {
+
+/**
+ * @brief Traits for timeouts policy
+ */
+struct timeouts_policy_traits
+{
+  /// @brief Name that can be used to identify the policy (for NS-3 object model and logging)
+  static std::string GetName () { return "Timeouts"; }
+
+  struct policy_hook_type : public boost::intrusive::set_member_hook<> { Time timeWhenShouldExpire; };
+
+  template<class Container>
+  struct container_hook
+  {
+    typedef boost::intrusive::member_hook< Container,
+                                           policy_hook_type,
+                                           &Container::policy_hook_ > type;
+  };
+
+  template<class Base,
+           class Container,
+           class Hook>
+  struct policy
+  {
+    static Time& get_timeout (typename Container::iterator item)
+    {
+      return static_cast<typename policy_container::value_traits::hook_type*>
+        (policy_container::value_traits::to_node_ptr(*item))->timeWhenShouldExpire;
+    }
+
+    static const Time& get_timeout (typename Container::const_iterator item)
+    {
+      return static_cast<const typename policy_container::value_traits::hook_type*>
+        (policy_container::value_traits::to_node_ptr(*item))->timeWhenShouldExpire;
+    }
+
+    template<class Key>
+    struct MemberHookLess
+    {
+      bool operator () (const Key &a, const Key &b) const
+      {
+        return get_timeout (&a) < get_timeout (&b);
+      }
+    };
+
+    typedef boost::intrusive::multiset< Container,
+                                        boost::intrusive::compare< MemberHookLess< Container > >,
+                                        Hook > policy_container;
+
+
+    class type : public policy_container
+    {
+    public:
+      typedef policy policy_base; // to get access to get_timeout methods from outside
+      typedef Container parent_trie;
+
+      type (Base &base)
+        : m_base (base)
+      {
+      }
+
+      inline void
+      update (typename parent_trie::iterator item)
+      {
+        // do nothing
+      }
+
+      inline bool
+      insert (typename parent_trie::iterator item)
+      {
+        Time timeout = item->payload ()->GetInterest ()->GetInterestLifetime ();
+        if (timeout.IsZero ()) timeout = Seconds (4.0);
+        
+        get_timeout (item) = Simulator::Now () + timeout;
+        policy_container::insert (*item);
+
+        if (policy_container::s_iterator_to (*item) == policy_container::begin ())
+          {
+            if (m_timeoutEvent.IsRunning ())
+              {
+                Simulator::Remove (m_timeoutEvent); // just canceling would not clean up list of events
+              }
+
+            m_timeoutEvent = Simulator::Schedule (timeout, &type::ProcessTimeoutEntry, this, item);
+          }
+        
+        return true;
+      }
+
+      inline void
+      lookup (typename parent_trie::iterator item)
+      {
+        // do nothing. it's random policy
+      }
+
+      inline void
+      erase (typename parent_trie::iterator item)
+      {
+        if (policy_container::s_iterator_to (*item) == policy_container::begin ())
+          {
+            if (m_timeoutEvent.IsRunning ())
+              {
+                Simulator::Remove (m_timeoutEvent); // just canceling would not clean up list of events
+              }
+          }
+
+        // erase only if freshness is non zero (otherwise an item is not in the policy
+        policy_container::erase (policy_container::s_iterator_to (*item));
+
+        if (!policy_container::empty ())
+          {
+            Time timeout = get_timeout (&*policy_container::begin ()) - Simulator::Now ();
+            m_timeoutEvent = Simulator::Schedule (timeout, &type::ProcessTimeoutEntry, this, &*policy_container::begin ());
+          }
+      }
+
+      inline void
+      clear ()
+      {
+        policy_container::clear ();
+      }
+
+      inline void
+      ProcessTimeoutEntry (typename parent_trie::iterator item)
+      {
+        item->payload ()->m_timeoutCallback (item->payload ()->GetInterest ());
+
+        m_base.erase (item);
+      }
+
+    private:
+      type () : m_base (*((Base*)0)) { };
+
+    private:
+      Base &m_base;
+      EventId m_timeoutEvent;
+    };
+  };
+};
+
+} // detail
+} // ndn
+} // ns3
+
+#endif // NDN_NDNCXX_DETAIL_TIMEOUTS_STATS_POLICY_H
diff --git a/ndn.cxx/ndn-api-face.cc b/ndn.cxx/ndn-api-face.cc
index 4c7cc10..6adfe99 100644
--- a/ndn.cxx/ndn-api-face.cc
+++ b/ndn.cxx/ndn-api-face.cc
@@ -19,7 +19,8 @@
  */
 
 #include "ndn-api-face.h"
-#include "detail/filter-entry.h"
+#include "detail/pending-interests-container.h"
+#include "detail/registered-prefix-container.h"
 
 #include <ns3/random-variable.h>
 
@@ -41,6 +42,8 @@
 namespace ns3 {
 namespace ndn {
 
+using namespace detail;
+
 class ApiFacePriv
 {
 public:
@@ -51,8 +54,8 @@
   
   ns3::UniformVariable m_rand; // nonce generator
 
-  FilterEntryContainer<ApiFace::DataCallback, Interest> m_pendingInterests;
-  FilterEntryContainer<ApiFace::InterestCallback, Name> m_expectedInterests;
+  PendingInterestContainer m_pendingInterests;
+  RegisteredPrefixContainer m_expectedInterests;
 };
 
 
@@ -74,6 +77,24 @@
 }
 
 void
+ApiFace::Shutdown ()
+{
+  NS_LOG_FUNCTION (this);
+
+  if (!IsUp ())
+    {
+      return;
+    }
+  
+  this->SetUp (false);
+
+  m_this->m_pendingInterests.clear ();
+  m_this->m_expectedInterests.clear ();
+
+  GetNode ()->GetObject<L3Protocol> ()->RemoveFace (this);
+}
+
+void
 ApiFace::ExpressInterest (Ptr<Interest> interest,
                           DataCallback onData,
                           TimeoutCallback onTimeout/* = MakeNullCallback< void, Ptr<Interest> > ()*/)
@@ -86,16 +107,15 @@
     }
   
   // Record the callback
-  FilterEntryContainer<DataCallback, Interest>::iterator entry =
-    m_this->m_pendingInterests.find_exact (interest->GetName ());
+  PendingInterestContainer::iterator entry = m_this->m_pendingInterests.find_exact (interest->GetName ());
   if (entry == m_this->m_pendingInterests.end ())
     {
-      pair<FilterEntryContainer<DataCallback, Interest>::iterator, bool> status =
-        m_this->m_pendingInterests.insert (interest->GetName (), Create< FilterEntry<DataCallback, Interest> > (interest));
+      pair<PendingInterestContainer::iterator, bool> status =
+        m_this->m_pendingInterests.insert (interest->GetName (), Create <PendingInterestEntry> (interest));
 
       entry = status.first;
     }
-  entry->payload ()->AddCallback (onData);
+  entry->payload ()->AddCallbacks (onData, onTimeout);
 
   ReceiveInterest (interest);
 }
@@ -105,11 +125,11 @@
 {
   NS_LOG_DEBUG ("== setInterestFilter " << *prefix << " (" << GetNode ()->GetId () << ")");
 
-  FilterEntryContainer<InterestCallback, Name>::iterator entry = m_this->m_expectedInterests.find_exact (*prefix);
+  RegisteredPrefixContainer::iterator entry = m_this->m_expectedInterests.find_exact (*prefix);
   if (entry == m_this->m_expectedInterests.end ())
     {
-      pair<FilterEntryContainer<InterestCallback, Name>::iterator, bool> status =
-        m_this->m_expectedInterests.insert (*prefix, Create < FilterEntry<InterestCallback, Name> > (prefix));
+      pair<RegisteredPrefixContainer::iterator, bool> status =
+        m_this->m_expectedInterests.insert (*prefix, Create < RegisteredPrefixEntry > (prefix));
 
       entry = status.first;
     }
@@ -125,7 +145,7 @@
 void
 ApiFace::ClearInterestFilter (Ptr<const Name> prefix)
 {
-  FilterEntryContainer<InterestCallback, Name>::iterator entry = m_this->m_expectedInterests.find_exact (*prefix);
+  RegisteredPrefixContainer::iterator entry = m_this->m_expectedInterests.find_exact (*prefix);
   if (entry == m_this->m_expectedInterests.end ())
     return;
 
@@ -161,14 +181,13 @@
     }
 
   // the app cannot set several filters for the same prefix
-  FilterEntryContainer<InterestCallback, Name>::iterator entry =
-    m_this->m_expectedInterests.longest_prefix_match (interest->GetName ());
+  RegisteredPrefixContainer::iterator entry = m_this->m_expectedInterests.longest_prefix_match (interest->GetName ());
   if (entry == m_this->m_expectedInterests.end ())
     {
       return false;
     }
   
-  entry->payload ()->m_callback (entry->payload ()->GetPayload (), interest);
+  entry->payload ()->m_callback (entry->payload ()->GetPrefix (), interest);
   return true;
 }
 
@@ -186,8 +205,7 @@
       return false;
     }
 
-  FilterEntryContainer<DataCallback, Interest>::iterator entry =
-    m_this->m_pendingInterests.longest_prefix_match (data->GetName ());
+  PendingInterestContainer::iterator entry = m_this->m_pendingInterests.longest_prefix_match (data->GetName ());
   if (entry == m_this->m_pendingInterests.end ())
     {
       return false;
@@ -195,7 +213,7 @@
 
   while (entry != m_this->m_pendingInterests.end ())
     {
-      entry->payload ()->m_callback (entry->payload ()->GetPayload (), data);
+      entry->payload ()->m_dataCallback (entry->payload ()->GetInterest (), data);
       m_this->m_pendingInterests.erase (entry);
 
       entry = m_this->m_pendingInterests.longest_prefix_match (data->GetName ());
diff --git a/ndn.cxx/ndn-api-face.h b/ndn.cxx/ndn-api-face.h
index 8c589bb..160540e 100644
--- a/ndn.cxx/ndn-api-face.h
+++ b/ndn.cxx/ndn-api-face.h
@@ -56,6 +56,12 @@
   ~ApiFace ();
 
   /**
+   * @brief Shutdown the API face
+   */
+  void
+  Shutdown ();
+  
+  /**
    * @brief Express Interest
    *
    * @param name the Interest name
diff --git a/test/ndnSIM-api.cc b/test/ndnSIM-api.cc
new file mode 100644
index 0000000..b472850
--- /dev/null
+++ b/test/ndnSIM-api.cc
@@ -0,0 +1,162 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Alexander Afanasyev
+ *
+ * GNU v3.0 license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "ndnSIM-api.h"
+#include "ns3/core-module.h"
+#include "ns3/ndnSIM-module.h"
+#include "ns3/point-to-point-module.h"
+
+#include <boost/lexical_cast.hpp>
+
+NS_LOG_COMPONENT_DEFINE ("ndn.ApiTest");
+
+namespace ns3
+{
+
+class ApiTestClient : public Application
+{
+public:
+  static TypeId
+  GetTypeId ()
+  {
+    static TypeId tid = TypeId ("ns3::ndn::test::ApiTestClient")
+      .SetParent<Application> ()
+      .AddConstructor<ApiTestClient> ()
+      ;
+    
+    return tid;
+  }
+
+  ApiTestClient ()
+    : datas (0)
+    , timeouts (0)
+  {
+  }
+  
+protected:
+  void
+  StartApplication ()
+  {
+    m_face = Create<ndn::ApiFace> (GetNode ());
+    
+    Simulator::Schedule (Seconds (0.1), &ApiTestClient::SendPacket, this, std::string ("/1"));
+    Simulator::Schedule (Seconds (5.0), &ApiTestClient::SendPacket, this, std::string ("/2"));
+  }
+
+  void
+  StopApplication ()
+  {
+    m_face->Shutdown ();
+    m_face = 0;
+  }
+
+private:
+  void
+  GotData (Ptr<const ndn::Interest>, Ptr<const ndn::ContentObject>)
+  {
+    datas++;
+  }
+
+  void
+  GotTimeout (Ptr<const ndn::Interest>)
+  {
+    timeouts++;
+  }
+
+  void
+  SendPacket (const std::string &prefix)
+  {
+    Ptr<ndn::Interest> interest = Create<ndn::Interest> ();
+    interest->SetName (Create<ndn::Name> (prefix));
+    interest->SetInterestLifetime (Seconds (0.5));
+
+    m_face->ExpressInterest (interest,
+                             MakeCallback (&ApiTestClient::GotData, this),
+                             MakeCallback (&ApiTestClient::GotTimeout, this));
+  }
+
+public:
+  uint32_t datas;
+  uint32_t timeouts;
+  
+private:
+  Ptr<ndn::ApiFace> m_face;
+};
+
+NS_OBJECT_ENSURE_REGISTERED (ApiTestClient);
+
+void
+ApiTest::Check0 (Ptr<Application> app)
+{
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->datas, 0, "");
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->timeouts, 0, "");
+}
+
+void
+ApiTest::Check1 (Ptr<Application> app)
+{
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->datas, 1, "");
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->timeouts, 0, "");
+}
+
+void
+ApiTest::Check2 (Ptr<Application> app)
+{
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->datas, 1, "");
+  NS_TEST_ASSERT_MSG_EQ (DynamicCast<ApiTestClient> (app)->timeouts, 1, "");
+}
+
+
+void
+ApiTest::DoRun ()
+{
+  Config::SetDefault ("ns3::PointToPointNetDevice::DataRate", StringValue ("1Mbps"));
+  Config::SetDefault ("ns3::PointToPointChannel::Delay", StringValue ("10ms"));
+  Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("20"));
+
+  // Creating nodes
+  NodeContainer nodes;
+  nodes.Create (3);
+
+  // Connecting nodes using two links
+  PointToPointHelper p2p;
+  p2p.Install (nodes.Get (0), nodes.Get (1));
+  p2p.Install (nodes.Get (1), nodes.Get (2));
+
+  // Install NDN stack on all nodes
+  ndn::StackHelper ndnHelper;
+  ndnHelper.SetDefaultRoutes (true);
+  ndnHelper.InstallAll ();
+
+  // Installing applications
+
+  // Consumer
+  ndn::AppHelper consumerHelper ("ns3::ndn::test::ApiTestClient");
+  ApplicationContainer apps = consumerHelper.Install (nodes.Get (0)); // first node
+
+  // Producer
+  ndn::AppHelper producerHelper ("ns3::ndn::Producer");
+  // Producer will reply to all requests starting with /prefix
+  producerHelper.SetPrefix ("/");
+  producerHelper.SetAttribute ("Postfix", StringValue ("/unique/postfix"));
+  producerHelper.SetAttribute ("PayloadSize", StringValue("1024"));
+  producerHelper.Install (nodes.Get (2)).Stop (Seconds (4.0)); // last node
+
+  Simulator::Schedule (Seconds (0.0001), &ApiTest::Check0, this, apps.Get (0));
+  Simulator::Schedule (Seconds (0.2000), &ApiTest::Check1, this, apps.Get (0));
+  Simulator::Schedule (Seconds (5.6100), &ApiTest::Check2, this, apps.Get (0));
+
+  Simulator::Stop (Seconds (20.0));
+
+  Simulator::Run ();
+  Simulator::Destroy ();
+}
+
+}
diff --git a/test/ndnSIM-api.h b/test/ndnSIM-api.h
new file mode 100644
index 0000000..7df0112
--- /dev/null
+++ b/test/ndnSIM-api.h
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Alexander Afanasyev
+ *
+ * GNU v3.0 license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDNSIM_TEST_API_H
+#define NDNSIM_TEST_API_H
+
+#include "ns3/test.h"
+#include "ns3/ptr.h"
+
+namespace ns3 {
+
+class Application;
+
+namespace ndn {
+}
+  
+class ApiTest : public TestCase
+{
+public:
+  ApiTest ()
+    : TestCase ("API test")
+  {
+  }
+    
+private:
+  virtual void DoRun ();
+
+  void Check0 (Ptr<Application> app);
+  void Check1 (Ptr<Application> app);
+  void Check2 (Ptr<Application> app);
+};
+  
+}
+
+#endif // NDNSIM_TEST_API_H
diff --git a/test/ndnSIM-tests.cc b/test/ndnSIM-tests.cc
index a23dd05..face35e 100644
--- a/test/ndnSIM-tests.cc
+++ b/test/ndnSIM-tests.cc
@@ -24,6 +24,7 @@
 #include "ndnSIM-serialization.h"
 #include "ndnSIM-pit.h"
 #include "ndnSIM-fib-entry.h"
+#include "ndnSIM-api.h"
 
 namespace ns3
 {
@@ -40,6 +41,7 @@
     AddTestCase (new ContentObjectSerializationTest (), TestCase::QUICK);
     AddTestCase (new FibEntryTest (), TestCase::QUICK);
     AddTestCase (new PitTest (), TestCase::QUICK);
+    AddTestCase (new ApiTest (), TestCase::QUICK);
   }
 };