Add MockFace
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..fbf9a2f
--- /dev/null
+++ b/src/main/java/com/intel/jndn/mock/MockFace.java
@@ -0,0 +1,574 @@
+/*
+ * 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();
+ }
+}