dependencies: Temporary solution to version incompatibility of jndn-utils and jndn-management
This commits adds modified versions of jndn-utils and jndn-management
to address version incompatibility (wire format for status datasets has
been changed).
Change-Id: Ic321c7a478b47961f0c4523e89ef8013593b14d3
diff --git a/app/build.gradle b/app/build.gradle
index 41a6db8..57b585b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -133,16 +133,18 @@
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:support-v4:21.0.3'
- compile('com.intel.jndn.utils:jndn-utils:0.9.7') {
- exclude group: 'com.intel.jndn.mock', module: 'jndn-mock'
- exclude group: 'net.named-data', module: 'jndn'
- }
+ /// Temporarily, the modified code is embedded within NFD app
+ //
+ // compile('com.intel.jndn.utils:jndn-utils:0.9.7') {
+ // exclude group: 'com.intel.jndn.mock', module: 'jndn-mock'
+ // exclude group: 'net.named-data', module: 'jndn'
+ // }
- compile('com.intel.jndn.management:jndn-management:0.9.7') {
- exclude group: 'net.named-data', module: 'jndn'
- exclude group: 'com.intel.jndn.utils', module: 'jndn-utils'
- exclude group: 'com.intel.jndn.mock', module: 'jndn-mock'
- }
+ // compile('com.intel.jndn.management:jndn-management:0.9.7') {
+ // exclude group: 'net.named-data', module: 'jndn'
+ // exclude group: 'com.intel.jndn.utils', module: 'jndn-utils'
+ // exclude group: 'com.intel.jndn.mock', module: 'jndn-mock'
+ // }
// compile('net.named-data:jndn-android:0.7') {
compile('net.named-data:jndn:0.4') {
diff --git a/app/src/main/java/com/intel/jndn/management/EncodingHelper.java b/app/src/main/java/com/intel/jndn/management/EncodingHelper.java
new file mode 100644
index 0000000..5d658ee
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/EncodingHelper.java
@@ -0,0 +1,144 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import java.nio.ByteBuffer;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.Tlv;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Provide helper methods to cover areas too protected in Tlv0_1_1WireFormat;
+ * this class can be deprecated if WireFormats allow passing in an existing
+ * TlvEncoder/TlvDecoder (currently these methods are protected).
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class EncodingHelper {
+
+ /**
+ * Helper to decode names since Tlv0_1_1WireFormat.java uses its own internal,
+ * protected implementation.
+ *
+ * @param input
+ * @return
+ * @throws EncodingException
+ */
+ public static Name decodeName(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ return decodeName(decoder);
+ }
+
+ /**
+ * Helper to decode names using an existing decoding context; could be merged
+ * to Tlv0_1_1WireFormat.java.
+ *
+ * @param decoder
+ * @return
+ * @throws EncodingException
+ */
+ public static Name decodeName(TlvDecoder decoder) throws EncodingException {
+ Name name = new Name();
+ int endOffset = decoder.readNestedTlvsStart(Tlv.Name);
+ while (decoder.getOffset() < endOffset) {
+ name.append(new Blob(decoder.readBlobTlv(Tlv.NameComponent), true));
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+ return name;
+ }
+
+ /**
+ * Helper to encode names since Tlv0_1_1WireFormat.java uses its own internal,
+ * protected implementation.
+ *
+ * @param name
+ * @return
+ */
+ public static Blob encodeName(Name name) {
+ TlvEncoder encoder = new TlvEncoder();
+ encodeName(name, encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Helper to encode names using an existing encoding context; could be merged
+ * to Tlv0_1_1WireFormat.java.
+ *
+ * @param name
+ * @param encoder
+ */
+ public static final void encodeName(Name name, TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ for (int i = name.size() - 1; i >= 0; --i) {
+ encoder.writeBlobTlv(Tlv.NameComponent, name.get(i).getValue().buf());
+ }
+ encoder.writeTypeAndLength(Tlv.Name, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Helper to encode control parameters using an existing encoding context;
+ * could be merged to Tlv0_1_1WireFormat.java.
+ *
+ * @param controlParameters
+ * @param encoder
+ */
+ public static final void encodeControlParameters(ControlParameters controlParameters, TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+
+ // Encode backwards.
+ encoder.writeOptionalNonNegativeIntegerTlvFromDouble(Tlv.ControlParameters_ExpirationPeriod,
+ controlParameters.getExpirationPeriod());
+
+ // Encode strategy
+ if (controlParameters.getStrategy().size() != 0) {
+ int strategySaveLength = encoder.getLength();
+ encodeName(controlParameters.getStrategy(), encoder);
+ encoder.writeTypeAndLength(Tlv.ControlParameters_Strategy,
+ encoder.getLength() - strategySaveLength);
+ }
+
+ // Encode ForwardingFlags
+ int flags = controlParameters.getForwardingFlags().getNfdForwardingFlags();
+ if (flags != new ForwardingFlags().getNfdForwardingFlags()) // The flags are not the default value.
+ {
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Flags, flags);
+ }
+
+ encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Cost, controlParameters.getCost());
+ encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Origin, controlParameters.getOrigin());
+ encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_LocalControlFeature,
+ controlParameters.getLocalControlFeature());
+
+ // Encode URI
+ if (!controlParameters.getUri().isEmpty()) {
+ encoder.writeBlobTlv(Tlv.ControlParameters_Uri,
+ new Blob(controlParameters.getUri()).buf());
+ }
+
+ encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId, controlParameters.getFaceId());
+
+ // Encode name
+ if (controlParameters.getName().size() != 0) {
+ encodeName(controlParameters.getName(), encoder);
+ }
+
+ encoder.writeTypeAndLength(Tlv.ControlParameters_ControlParameters, encoder.getLength() - saveLength);
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/management/ManagementException.java b/app/src/main/java/com/intel/jndn/management/ManagementException.java
new file mode 100644
index 0000000..70a15d1
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/ManagementException.java
@@ -0,0 +1,73 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.management.types.ControlResponse;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a failure to correctly manage the NDN Forwarding Daemon (NFD).
+ * Inspect this object with getCause() to see why the management operation
+ * failed.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class ManagementException extends Exception {
+
+ /**
+ *
+ * @param message
+ */
+ public ManagementException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * @param message
+ * @param cause
+ */
+ public ManagementException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Parse an NFD response to create a ManagementException.
+ *
+ * @param forwarderResponse
+ * @return
+ */
+ public static ManagementException fromResponse(Blob forwarderResponse) {
+ ControlResponse response = new ControlResponse();
+ try {
+ response.wireDecode(forwarderResponse.buf());
+ String message = "Action failed, forwarder returned: " + response.getStatusCode() + " " + response.getStatusText();
+ return new ManagementException(message);
+ } catch (EncodingException e) {
+ return new ManagementException("Action failed and forwarder response was unparseable.", e);
+ }
+ }
+
+ /**
+ * Parse an NFD response to create a ManagementException.
+ *
+ * @param forwarderResponse
+ * @return
+ */
+ public static ManagementException fromResponse(ControlResponse response) {
+ String message = "Action failed, forwarder returned: " + response.getStatusCode() + " " + response.getStatusText();
+ return new ManagementException(message);
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/management/NFD.java b/app/src/main/java/com/intel/jndn/management/NFD.java
new file mode 100644
index 0000000..b62cbe6
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/NFD.java
@@ -0,0 +1,520 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management;
+
+import com.intel.jndn.management.types.StatusDataset;
+import com.intel.jndn.management.types.ControlResponse;
+import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.FibEntry;
+import com.intel.jndn.management.types.ForwarderStatus;
+import com.intel.jndn.management.types.LocalControlHeader;
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.utils.SimpleClient;
+import com.intel.jndn.utils.SegmentedClient;
+import java.io.IOException;
+import java.util.List;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.SecurityException;
+import java.util.logging.Logger;
+
+import net.named_data.nfd.utils.G;
+
+/**
+ * Helper class for interacting with an NDN forwarder daemon; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/Management">http://redmine.named-data.net/projects/nfd/wiki/Management</a>
+ * for explanations of the various protocols used.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NFD {
+
+ public final static long DEFAULT_TIMEOUT = 2000;
+ public final static int OK_STATUS = 200;
+ static private final Logger logger = Logger.getLogger(NFD.class.getName());
+
+ /**
+ * Ping a forwarder on an existing face to verify that the forwarder is
+ * working and responding to requests; this version sends a discovery packet
+ * to /localhost/nfd which should always respond if the requestor is on the
+ * same machine as the NDN forwarding daemon.
+ *
+ * @param face only a localhost Face
+ * @return true if successful, false otherwise
+ */
+ public static boolean pingLocal(Face face) {
+ return ping(face, new Name("/localhost/nfd"));
+ }
+
+ /**
+ * Request a name on an existing face to verify the forwarder is working and
+ * responding to requests. Note that the name must be served or cached on the
+ * forwarder for this to return true.
+ *
+ * @param face
+ * @param name
+ * @return true if successful, false otherwise
+ */
+ public static boolean ping(Face face, Name name) {
+ // build interest
+ Interest interest = new Interest(name);
+ interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+ interest.setMustBeFresh(true);
+
+ // send packet
+ try {
+ Data data = SimpleClient.getDefault().getSync(face, interest);
+ return data != null;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve the status of the given forwarder; calls /localhost/nfd/status
+ * which requires a local Face (all non-local packets are dropped)
+ *
+ * @param forwarder only a localhost Face
+ * @return the forwarder status object, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus">
+ * http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus</a>.
+ * @throws java.lang.Exception
+ */
+ public static ForwarderStatus getForwarderStatus(Face forwarder) throws Exception {
+ Interest interest = new Interest(new Name("/localhost/nfd/status"));
+ interest.setMustBeFresh(true);
+ interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
+ interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+
+ Data data = SimpleClient.getDefault().getSync(forwarder, interest);
+ G.Log("-----------" + data.getName().toUri());
+ ForwarderStatus status = new ForwarderStatus();
+ status.wireDecode(data.getContent().buf());
+ G.Log("----------- done");
+ return status;
+ }
+
+ /**
+ * Retrieve a list of faces and their status from the given forwarder; calls
+ * /localhost/nfd/faces/list which requires a local Face (all non-local
+ * packets are dropped)
+ *
+ * @param forwarder only a localhost Face
+ * @return a list of face status objects, see
+ * http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt.
+ * @throws java.lang.Exception
+ */
+ public static List<FaceStatus> getFaceList(Face forwarder) throws Exception {
+ Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/faces/list"));
+ return StatusDataset.wireDecode(data.getContent(), FaceStatus.class);
+ }
+
+ /**
+ * Retrieve a list of FIB entries and their NextHopRecords from the given
+ * forwarder; calls /localhost/nfd/fib/list which requires a local Face (all
+ * non-local packets are dropped).
+ *
+ * @param forwarder only a localhost Face
+ * @return a list of FIB entries, see
+ * http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset.
+ * @throws java.lang.Exception
+ */
+ public static List<FibEntry> getFibList(Face forwarder) throws Exception {
+ Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/fib/list"));
+ return StatusDataset.wireDecode(data.getContent(), FibEntry.class);
+ }
+
+ /**
+ * Retrieve a list of routing entries from the RIB; calls
+ * /localhost/nfd/rib/list which requires a local Face (all non-local packets
+ * are dropped).
+ *
+ * @param forwarder only a localhost Face
+ * @return a list of RIB entries, i.e. routes, see
+ * http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset.
+ * @throws java.lang.Exception
+ */
+ public static List<RibEntry> getRouteList(Face forwarder) throws Exception {
+ Data data = retrieveDataSet(forwarder, new Name("/localhost/nfd/rib/list"));
+ return StatusDataset.wireDecode(data.getContent(), RibEntry.class);
+ }
+
+ /**
+ * Helper method to register a new face on the forwarder; as mentioned at
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>,
+ * this is more for debugging; use 'register' instead
+ *
+ * @param forwarder only a localhost Face
+ * @param faceId
+ * @param prefix
+ * @throws java.lang.Exception
+ */
+ public static void addNextHop(Face forwarder, int faceId, Name prefix) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/fib/add-nexthop");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(prefix);
+ parameters.setFaceId(faceId);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Create a new face on the given forwarder. Ensure the forwarding face is on
+ * the local machine (management requests are to /localhost/...) and that
+ * command signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder only a localhost Face
+ * @param uri
+ * @return
+ * @throws java.lang.Exception
+ */
+ public static int createFace(Face forwarder, String uri) throws Exception {
+ Name command = new Name("/localhost/nfd/faces/create");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setUri(uri);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ ControlResponse response = sendCommand(forwarder, new Interest(command));
+
+ // return
+ return response.getBody().get(0).getFaceId();
+ }
+
+ /**
+ * Destroy a face on given forwarder. Ensure the forwarding face is on the
+ * local machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder only a localhost Face
+ * @param faceId
+ * @throws java.lang.Exception
+ */
+ public static void destroyFace(Face forwarder, int faceId) throws Exception {
+ Name command = new Name("/localhost/nfd/faces/destroy");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setFaceId(faceId);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Enable a local control feature on the given forwarder. See
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature</a>
+ *
+ * @param forwarder
+ * @param header
+ * @throws Exception
+ */
+ public static void enableLocalControlHeader(Face forwarder, LocalControlHeader header) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/faces/enable-local-control");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setLocalControlFeature(header.getNumericValue());
+ command.append(parameters.wireEncode());
+
+ // send command and return
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Disable a local control feature on the given forwarder. See
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature</a>
+ *
+ * @param forwarder
+ * @param header
+ * @throws Exception
+ */
+ public static void disableLocalControlHeader(Face forwarder, LocalControlHeader header) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/faces/disable-local-control");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setLocalControlFeature(header.getNumericValue());
+ command.append(parameters.wireEncode());
+
+ // send command and return
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Register a route on the forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder only a localhost Face
+ * @param controlParameters
+ * @throws Exception
+ */
+ public static void register(Face forwarder, ControlParameters controlParameters) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/rib/register");
+ command.append(controlParameters.wireEncode());
+
+ // send the interest
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Register a route on a forwarder; this will create a new face on the
+ * forwarder to the given URI/route pair. See register(Face,
+ * ControlParameters) for more details documentation.
+ *
+ * @param forwarder only a localhost Face
+ * @param uri
+ * @param cost
+ * @param route
+ * @throws java.lang.Exception
+ */
+ public static void register(Face forwarder, String uri, Name route, int cost) throws Exception {
+ // create the new face
+ int faceId = createFace(forwarder, uri);
+
+ // run base method
+ register(forwarder, faceId, route, cost);
+ }
+
+ /**
+ * Register a route on a forwarder; this will not create a new face since it
+ * is provided a faceId. See register(Face, ControlParameters) for full
+ * documentation
+ *
+ * @param forwarder only a localhost Face
+ * @param faceId
+ * @param route
+ * @param cost
+ * @throws java.lang.Exception
+ */
+ public static void register(Face forwarder, int faceId, Name route, int cost) throws Exception {
+ // build command name
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(route);
+ parameters.setFaceId(faceId);
+ parameters.setCost(cost);
+ ForwardingFlags flags = new ForwardingFlags();
+ flags.setCapture(true);
+ flags.setChildInherit(true);
+ parameters.setForwardingFlags(flags);
+
+ // run base method
+ register(forwarder, parameters);
+ }
+
+ /**
+ * Unregister a route on a forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()
+ *
+ * @param forwarder
+ * @param controlParameters
+ * @throws java.lang.Exception
+ */
+ public static void unregister(Face forwarder, ControlParameters controlParameters) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/rib/unregister");
+ command.append(controlParameters.wireEncode());
+
+ // send the interest
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Unregister a route on a forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()
+ *
+ * @param forwarder
+ * @param route
+ * @throws java.lang.Exception
+ */
+ public static void unregister(Face forwarder, Name route) throws Exception {
+ // build command name
+ ControlParameters controlParameters = new ControlParameters();
+ controlParameters.setName(route);
+
+ // send the interest
+ unregister(forwarder, controlParameters);
+ }
+
+ /**
+ * Unregister a route on a forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()
+ *
+ * @param forwarder
+ * @param route
+ * @param faceId
+ * @throws java.lang.Exception
+ */
+ public static void unregister(Face forwarder, Name route, int faceId) throws Exception {
+ // build command name
+ ControlParameters controlParameters = new ControlParameters();
+ controlParameters.setName(route);
+ controlParameters.setFaceId(faceId);
+
+ // send the interest
+ unregister(forwarder, controlParameters);
+ }
+
+ /**
+ * Unregister a route on a forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()
+ *
+ * @param forwarder
+ * @param route
+ * @param uri
+ * @throws java.lang.Exception
+ */
+ public static void unregister(Face forwarder, Name route, String uri) throws Exception {
+ int faceId = -1;
+ for (FaceStatus face : getFaceList(forwarder)) {
+ if (face.getUri().matches(uri)) {
+ faceId = face.getFaceId();
+ break;
+ }
+ }
+
+ if (faceId == -1) {
+ throw new ManagementException("Face not found: " + uri);
+ }
+
+ // send the interest
+ unregister(forwarder, route, faceId);
+ }
+
+ /**
+ * Set a strategy on the forwarder; see
+ * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
+ * for command-line usage and
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice</a>
+ * for protocol documentation. Ensure the forwarding face is on the local
+ * machine (management requests are to /localhost/...) and that command
+ * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder only a localhost Face
+ * @param prefix
+ * @param strategy
+ * @throws Exception
+ */
+ public static void setStrategy(Face forwarder, Name prefix, Name strategy) throws Exception {
+ // build command name
+ Name command = new Name("/localhost/nfd/strategy-choice/set");
+ ControlParameters parameters = new ControlParameters();
+ parameters.setName(prefix);
+ parameters.setStrategy(strategy);
+ command.append(parameters.wireEncode());
+
+ // send the interest
+ sendCommand(forwarder, new Interest(command));
+ }
+
+ /**
+ * Build an interest to retrieve a segmented data set from the NFD; for
+ * details on the DataSet, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+ *
+ * @param forwarder
+ * @param datasetName
+ * @return
+ * @throws IOException
+ * @throws ManagementException
+ */
+ public static Data retrieveDataSet(Face forwarder, Name datasetName) throws IOException, ManagementException {
+ // build management Interest packet; see <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+ Interest interest = new Interest(datasetName);
+ interest.setMustBeFresh(true);
+ interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
+ interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
+
+ // send packet
+ Data data = SegmentedClient.getDefault().getSync(forwarder, interest);
+
+ // check for failed request
+ if (data.getContent().buf().get(0) == ControlResponse.TLV_CONTROL_RESPONSE) {
+ throw ManagementException.fromResponse(data.getContent());
+ }
+
+ return data;
+ }
+
+ /**
+ * Send an interest as a command to the forwarder; this method will convert
+ * the interest to a command interest and block until a response is received
+ * from the forwarder. Ensure the forwarding face is on the local machine
+ * (management requests are to /localhost/...) and that command signing has
+ * been set up (e.g. forwarder.setCommandSigningInfo()).
+ *
+ * @param forwarder only a localhost Face, command signing info must be set
+ * @param interest As described at
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,</a>
+ * the requested interest must have encoded ControlParameters appended to the
+ * interest name
+ * @return
+ * @throws java.io.IOException
+ * @throws net.named_data.jndn.encoding.EncodingException
+ * @throws com.intel.jndn.management.ManagementException
+ */
+ public static ControlResponse sendCommand(Face forwarder, Interest interest) throws IOException, EncodingException, ManagementException {
+ // forwarder must have command signing info set
+ try {
+ forwarder.makeCommandInterest(interest);
+ } catch (SecurityException e) {
+ throw new IllegalArgumentException("Failed to make command interest; ensure command signing info is set on the face.", e);
+ }
+
+ // send command packet
+ Data data = SimpleClient.getDefault().getSync(forwarder, interest);
+
+ // decode response
+ ControlResponse response = new ControlResponse();
+ response.wireDecode(data.getContent().buf());
+
+ // check response for success
+ if (response.getStatusCode() != OK_STATUS) {
+ throw ManagementException.fromResponse(response);
+ }
+
+ return response;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/ControlResponse.java b/app/src/main/java/com/intel/jndn/management/types/ControlResponse.java
new file mode 100644
index 0000000..5514244
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/ControlResponse.java
@@ -0,0 +1,170 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.EncodingHelper;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.Tlv;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a ControlResponse, a Data packet sent in response to a
+ * ControlCommand to the NFD, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class ControlResponse {
+
+ /**
+ * Use TLV codes from jndn.encoding.tlv.Tlv.java See
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand</a>
+ */
+ public final static int TLV_CONTROL_RESPONSE = 101;
+ public final static int TLV_CONTROL_RESPONSE_STATUS_CODE = 102;
+ public final static int TLV_CONTROL_RESPONSE_STATUS_TEXT = 103;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ for (ControlParameters parameters : body) {
+ EncodingHelper.encodeControlParameters(parameters, encoder);
+ }
+ encoder.writeBlobTlv(TLV_CONTROL_RESPONSE_STATUS_TEXT, new Blob(statusText).buf());
+ encoder.writeNonNegativeIntegerTlv(TLV_CONTROL_RESPONSE_STATUS_CODE, statusCode);
+ encoder.writeTypeAndLength(TLV_CONTROL_RESPONSE, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws net.named_data.jndn.encoding.EncodingException
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder, input);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @param input the WireFormat version that decodes ControlParameters does not
+ * allow passing a TlvDecoder, so we must pass the buffer itself
+ * @throws EncodingException
+ */
+ public void wireDecode(TlvDecoder decoder, ByteBuffer input) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_CONTROL_RESPONSE);
+
+ // parse known TLVs
+ this.statusCode = (int) decoder.readNonNegativeIntegerTlv(TLV_CONTROL_RESPONSE_STATUS_CODE);
+ Blob statusText_ = new Blob(decoder.readBlobTlv(TLV_CONTROL_RESPONSE_STATUS_TEXT), true); // copy because buffer is immutable
+ this.statusText = statusText_.toString();
+
+ // use the already-written decoder for ControlParameters (but we have to copy the buffer)
+ while (decoder.peekType(Tlv.ControlParameters_ControlParameters, endOffset)) {
+ ByteBuffer copyInput = input.duplicate();
+ copyInput.position(decoder.getOffset());
+ int internalEndOffset = decoder.readNestedTlvsStart(Tlv.ControlParameters_ControlParameters);
+ ControlParameters copyParameters = new ControlParameters();
+ copyParameters.wireDecode(copyInput);
+ this.body.add(copyParameters);
+ decoder.seek(internalEndOffset);
+ decoder.finishNestedTlvs(internalEndOffset);
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get status code
+ *
+ * @return
+ */
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * Set status code
+ *
+ * @param statusCode
+ */
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ /**
+ * Get status text
+ *
+ * @return
+ */
+ public String getStatusText() {
+ return statusText;
+ }
+
+ /**
+ * Set status text
+ *
+ * @param statusText
+ */
+ public void setStatusText(String statusText) {
+ this.statusText = statusText;
+ }
+
+ /**
+ * Get body
+ *
+ * @return
+ */
+ public List<ControlParameters> getBody() {
+ return body;
+ }
+
+ /**
+ * Set body
+ *
+ * @param body
+ */
+ public void setBody(List<ControlParameters> body) {
+ this.body = body;
+ }
+
+ private int statusCode = -1;
+ private String statusText = "";
+ private List<ControlParameters> body = new ArrayList<>();
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/Decodable.java b/app/src/main/java/com/intel/jndn/management/types/Decodable.java
new file mode 100644
index 0000000..aec50dd
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/Decodable.java
@@ -0,0 +1,28 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+
+/**
+ * Interface used by StatusDataset to decode generic message types; if they are
+ * Decodable, then StatusDataset will instantiate and decode them.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface Decodable {
+
+ public void wireDecode(TlvDecoder decoder) throws EncodingException;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/FacePersistency.java b/app/src/main/java/com/intel/jndn/management/types/FacePersistency.java
new file mode 100644
index 0000000..d294fcb
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/FacePersistency.java
@@ -0,0 +1,36 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+/**
+ * Indicate whether the face is persistent; used by FaceStatus. See
+ * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public enum FacePersistency {
+
+ PERSISTENT(0),
+ ON_DEMAND(1),
+ PERMANENT(2);
+
+ FacePersistency(int value) {
+ value_ = value;
+ }
+
+ public final int getNumericValue() {
+ return value_;
+ }
+ private final int value_;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/FaceScope.java b/app/src/main/java/com/intel/jndn/management/types/FaceScope.java
new file mode 100644
index 0000000..bcba855
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/FaceScope.java
@@ -0,0 +1,36 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+/**
+ * Indicate whether the face is local for scope control purposes; used by
+ * FaceStatus See
+ * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
+ *
+ * @author andrew
+ */
+public enum FaceScope {
+
+ LOCAL(0),
+ NON_LOCAL(1);
+
+ FaceScope(int value) {
+ value_ = value;
+ }
+
+ public final int getNumericValue() {
+ return value_;
+ }
+ private final int value_;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/FaceStatus.java b/app/src/main/java/com/intel/jndn/management/types/FaceStatus.java
new file mode 100644
index 0000000..6a101a0
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/FaceStatus.java
@@ -0,0 +1,401 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import java.nio.ByteBuffer;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a FaceStatus object from /localhost/nfd/faces/list; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt</a>
+ * for details
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class FaceStatus implements Decodable {
+
+ /**
+ * Spec from
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand</a>
+ */
+ public static final int TLV_FACE_ID = 105;
+ public static final int TLV_URI = 114;
+ public static final int TLV_EXPIRATION_PERIOD = 109;
+
+ /**
+ * Spec from
+ * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
+ */
+ public static final int TLV_FACE_STATUS = 128;
+ public static final int TLV_LOCAL_URI = 129;
+ public static final int TLV_CHANNEL_STATUS = 130;
+ public static final int TLV_FACE_SCOPE = 132;
+ public static final int TLV_FACE_PERSISTENCY = 133;
+ public static final int TLV_LINK_TYPE = 134;
+ public static final int TLV_N_IN_INTERESTS = 144;
+ public static final int TLV_N_IN_DATAS = 145;
+ public static final int TLV_N_OUT_INTERESTS = 146;
+ public static final int TLV_N_OUT_DATAS = 147;
+ public static final int TLV_N_IN_BYTES = 148;
+ public static final int TLV_N_OUT_BYTES = 149;
+ public static final int TLV_NUM_IN_NACKS = 151;
+ public static final int TLV_NUM_OUT_NACKS = 152;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_BYTES, outBytes);
+ encoder.writeNonNegativeIntegerTlv(TLV_N_IN_BYTES, inBytes);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_NACKS, numOutNacks);
+ encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_DATAS, outDatas);
+ encoder.writeNonNegativeIntegerTlv(TLV_N_OUT_INTERESTS, outInterests);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_NACKS, numInNacks);
+ encoder.writeNonNegativeIntegerTlv(TLV_N_IN_DATAS, inDatas);
+ encoder.writeNonNegativeIntegerTlv(TLV_N_IN_INTERESTS, inInterests);
+ encoder.writeNonNegativeIntegerTlv(TLV_LINK_TYPE, linkType.getNumericValue());
+ encoder.writeNonNegativeIntegerTlv(TLV_FACE_PERSISTENCY, facePersistency.getNumericValue());
+ encoder.writeNonNegativeIntegerTlv(TLV_FACE_SCOPE, faceScope.getNumericValue());
+ encoder.writeOptionalNonNegativeIntegerTlv(TLV_EXPIRATION_PERIOD, expirationPeriod);
+ encoder.writeBlobTlv(TLV_LOCAL_URI, new Blob(localUri).buf());
+ encoder.writeBlobTlv(TLV_URI, new Blob(uri).buf());
+ encoder.writeNonNegativeIntegerTlv(TLV_FACE_ID, faceId);
+ encoder.writeTypeAndLength(TLV_FACE_STATUS, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws net.named_data.jndn.encoding.EncodingException
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ @Override
+ public void wireDecode(TlvDecoder decoder) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_FACE_STATUS);
+ // parse
+ this.faceId = (int) decoder.readNonNegativeIntegerTlv(TLV_FACE_ID);
+ Blob uri_ = new Blob(decoder.readBlobTlv(TLV_URI), true); // copy because buffer is immutable
+ this.uri = uri_.toString();
+ Blob localUri_ = new Blob(decoder.readBlobTlv(TLV_LOCAL_URI), true); // copy because buffer is immutable
+ this.localUri = localUri_.toString();
+ this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(TLV_EXPIRATION_PERIOD, endOffset);
+ this.faceScope = FaceScope.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_FACE_SCOPE)];
+ this.facePersistency = FacePersistency.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_FACE_PERSISTENCY)];
+ this.linkType = LinkType.values()[(int) decoder.readNonNegativeIntegerTlv(TLV_LINK_TYPE)];
+ this.inInterests = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_INTERESTS);
+ this.inDatas = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_DATAS);
+ this.numInNacks = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_NACKS);
+ this.outInterests = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_INTERESTS);
+ this.outDatas = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_DATAS);
+ this.numOutNacks = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_NACKS);
+ this.inBytes = (int) decoder.readNonNegativeIntegerTlv(TLV_N_IN_BYTES);
+ this.outBytes = (int) decoder.readNonNegativeIntegerTlv(TLV_N_OUT_BYTES);
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get face ID
+ *
+ * @return
+ */
+ public int getFaceId() {
+ return faceId;
+ }
+
+ /**
+ * Set face ID
+ *
+ * @param faceId
+ */
+ public void setFaceId(int faceId) {
+ this.faceId = faceId;
+ }
+
+ /**
+ * Get face ID
+ *
+ * @return
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * Set URI
+ *
+ * @param uri
+ */
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Get face ID
+ *
+ * @return
+ */
+ public String getLocalUri() {
+ return localUri;
+ }
+
+ /**
+ * Set local URI
+ *
+ * @param localUri
+ */
+ public void setLocalUri(String localUri) {
+ this.localUri = localUri;
+ }
+
+ /**
+ * Get expiration period
+ *
+ * @return
+ */
+ public int getExpirationPeriod() {
+ return expirationPeriod;
+ }
+
+ /**
+ * Set expiration period
+ *
+ * @param expirationPeriod
+ */
+ public void setExpirationPeriod(int expirationPeriod) {
+ this.expirationPeriod = expirationPeriod;
+ }
+
+ /**
+ * Get face scope value
+ *
+ * @return
+ */
+ public FaceScope getFaceScope() {
+ return faceScope;
+ }
+
+ /**
+ * Set face scope value
+ *
+ * @param faceScope
+ */
+ public void setFaceScope(FaceScope faceScope) {
+ this.faceScope = faceScope;
+ }
+
+ /**
+ * Get face persistency value
+ *
+ * @return
+ */
+ public FacePersistency getFacePersistency() {
+ return facePersistency;
+ }
+
+ /**
+ * Set face persistency value
+ *
+ * @param facePersistency
+ */
+ public void setFacePersistency(FacePersistency facePersistency) {
+ this.facePersistency = facePersistency;
+ }
+
+ /**
+ * Get link type
+ *
+ * @return
+ */
+ public LinkType getLinkType() {
+ return linkType;
+ }
+
+ /**
+ * Set link type
+ *
+ * @param linkType
+ */
+ public void setLinkType(LinkType linkType) {
+ this.linkType = linkType;
+ }
+
+ /**
+ * Get number of received Interest packets
+ *
+ * @return
+ */
+ public int getInInterests() {
+ return inInterests;
+ }
+
+ /**
+ * Set number of received Interest packets
+ *
+ * @param inInterests
+ */
+ public void setInInterests(int inInterests) {
+ this.inInterests = inInterests;
+ }
+
+ /**
+ * Get number of sent Interest packets
+ *
+ * @return
+ */
+ public int getOutInterests() {
+ return outInterests;
+ }
+
+ /**
+ * Set number of sent Interest packets
+ *
+ * @param outInterests
+ */
+ public void setOutInterests(int outInterests) {
+ this.outInterests = outInterests;
+ }
+
+ /**
+ * Get number of received Data packets
+ *
+ * @return
+ */
+ public int getInDatas() {
+ return inDatas;
+ }
+
+ /**
+ * Set number of received Data packets
+ *
+ * @param inDatas
+ */
+ public void setInDatas(int inDatas) {
+ this.inDatas = inDatas;
+ }
+
+ /**
+ * Get number of sent Data packets
+ *
+ * @return
+ */
+ public int getOutDatas() {
+ return outDatas;
+ }
+
+ /**
+ * Set number of sent Data packets
+ *
+ * @param outDatas
+ */
+ public void setOutDatas(int outDatas) {
+ this.outDatas = outDatas;
+ }
+
+ /**
+ * Get number of input bytes
+ *
+ * @return
+ */
+ public int getInBytes() {
+ return inBytes;
+ }
+
+ /**
+ * Set number of input bytes
+ *
+ * @param inBytes
+ */
+ public void setInBytes(int inBytes) {
+ this.inBytes = inBytes;
+ }
+
+ /**
+ * Get number of output bytes
+ *
+ * @return
+ */
+ public int getOutBytes() {
+ return outBytes;
+ }
+
+ /**
+ * Set number of output bytes
+ *
+ * @param outBytes
+ */
+ public void setOutBytes(int outBytes) {
+ this.outBytes = outBytes;
+ }
+
+ public long getNumInNacks() {
+ return numInNacks;
+ }
+
+ public void setNumInNacks(long numInNacks) {
+ this.numInNacks = numInNacks;
+ }
+
+ public long getNumOutNacks() {
+ return numOutNacks;
+ }
+
+ public void setNumOutNacks(long numOutNacks) {
+ this.numOutNacks = numOutNacks;
+ }
+
+ private int faceId = -1;
+ private String uri = ""; // can't use URI because some are invalid syntax
+ private String localUri = ""; // can't use URI because some are invalid syntax
+ private int expirationPeriod = 0;
+ private FaceScope faceScope = FaceScope.LOCAL;
+ private FacePersistency facePersistency = FacePersistency.ON_DEMAND;
+ private LinkType linkType = LinkType.POINT_TO_POINT;
+ private int inInterests = 0;
+ private int outInterests = 0;
+ private int inDatas = 0;
+ private int outDatas = 0;
+ private int inBytes = 0;
+ private int outBytes = 0;
+ private long numInNacks = 0;
+ private long numOutNacks = 0;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/FibEntry.java b/app/src/main/java/com/intel/jndn/management/types/FibEntry.java
new file mode 100644
index 0000000..8c74636
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/FibEntry.java
@@ -0,0 +1,129 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.EncodingHelper;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a FibEntry returned from /localhost/nfd/fib/list; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class FibEntry implements Decodable {
+
+ public final static int TLV_FIB_ENTRY = 128;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ for (NextHopRecord record : records) {
+ record.wireEncode(encoder);
+ }
+ EncodingHelper.encodeName(name, encoder);
+ encoder.writeTypeAndLength(TLV_FIB_ENTRY, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws EncodingException For invalid encoding.
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ @Override
+ public final void wireDecode(TlvDecoder decoder) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_FIB_ENTRY);
+ name = EncodingHelper.decodeName(decoder);
+ while (decoder.getOffset() < endOffset) {
+ NextHopRecord record = new NextHopRecord();
+ record.wireDecode(decoder);
+ records.add(record);
+ }
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get name
+ *
+ * @return
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param name
+ */
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ /**
+ * Get records
+ *
+ * @return
+ */
+ public List<NextHopRecord> getRecords() {
+ return records;
+ }
+
+ /**
+ * Set records
+ *
+ * @param records
+ */
+ public void setRecords(List<NextHopRecord> records) {
+ this.records = records;
+ }
+
+ private Name name = new Name();
+ private List<NextHopRecord> records = new ArrayList<>();
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java b/app/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java
new file mode 100644
index 0000000..6240bbd
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/ForwarderStatus.java
@@ -0,0 +1,240 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import java.nio.ByteBuffer;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a ForwarderStatus object from
+ * http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class ForwarderStatus implements Decodable {
+
+ public static final int TLV_NFD_VERSION = 0x80;
+ public static final int TLV_START_TIMESTAMP = 0x81;
+ public static final int TLV_CURRENT_TIMESTAMP = 0x82;
+ public static final int TLV_NUM_NAME_TREE_ENTRIES = 0x83;
+ public static final int TLV_NUM_FIB_ENTRIES = 0x84;
+ public static final int TLV_NUM_PIT_ENTRIES = 0x85;
+ public static final int TLV_NUM_MEASUREMENT_ENTRIES = 0x86;
+ public static final int TLV_NUM_CS_ENTRIES = 0x87;
+ public static final int TLV_NUM_IN_INTERESTS = 0x90;
+ public static final int TLV_NUM_IN_DATAS = 0x91;
+ public static final int TLV_NUM_OUT_INTERESTS = 0x92;
+ public static final int TLV_NUM_OUT_DATAS = 0x93;
+ public static final int TLV_NUM_IN_NACKS = 0x97;
+ public static final int TLV_NUM_OUT_NACKS = 0x98;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_NACKS, numOutNacks);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_DATAS, numOutDatas);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_OUT_INTERESTS, numOutInterests);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_NACKS, numInNacks);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_DATAS, numInDatas);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_IN_INTERESTS, numInInterests);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_CS_ENTRIES, numCsEntries);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_MEASUREMENT_ENTRIES, numMeasurementEntries);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_PIT_ENTRIES, numPitEntries);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_FIB_ENTRIES, numFibEntries);
+ encoder.writeNonNegativeIntegerTlv(TLV_NUM_NAME_TREE_ENTRIES, numNameTreeEntries);
+ encoder.writeNonNegativeIntegerTlv(TLV_CURRENT_TIMESTAMP, currentTimestamp);
+ encoder.writeNonNegativeIntegerTlv(TLV_START_TIMESTAMP, startTimestamp);
+ encoder.writeBlobTlv(TLV_NFD_VERSION, new Blob(nfdVersion).buf());
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws EncodingException For invalid encoding.
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ @Override
+ public void wireDecode(TlvDecoder decoder) throws EncodingException {
+ this.nfdVersion = new Blob(decoder.readBlobTlv(TLV_NFD_VERSION), true).toString();
+ this.startTimestamp = decoder.readNonNegativeIntegerTlv(TLV_START_TIMESTAMP);
+ this.currentTimestamp = decoder.readNonNegativeIntegerTlv(TLV_CURRENT_TIMESTAMP);
+ this.numNameTreeEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_NAME_TREE_ENTRIES);
+ this.numFibEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_FIB_ENTRIES);
+ this.numPitEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_PIT_ENTRIES);
+ this.numMeasurementEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_MEASUREMENT_ENTRIES);
+ this.numCsEntries = decoder.readNonNegativeIntegerTlv(TLV_NUM_CS_ENTRIES);
+ this.numInInterests = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_INTERESTS);
+ this.numInDatas = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_DATAS);
+ this.numInNacks = decoder.readNonNegativeIntegerTlv(TLV_NUM_IN_NACKS);
+ this.numOutInterests = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_INTERESTS);
+ this.numOutDatas = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_DATAS);
+ this.numOutNacks = decoder.readNonNegativeIntegerTlv(TLV_NUM_OUT_NACKS);
+ }
+
+ public String getNfdVersion() {
+ return nfdVersion;
+ }
+
+ public void setNfdVersion(String nfdVersion) {
+ this.nfdVersion = nfdVersion;
+ }
+
+ public long getStartTimestamp() {
+ return startTimestamp;
+ }
+
+ public void setStartTimestamp(long startTimestamp) {
+ this.startTimestamp = startTimestamp;
+ }
+
+ public long getCurrentTimestamp() {
+ return currentTimestamp;
+ }
+
+ public void setCurrentTimestamp(long currentTimestamp) {
+ this.currentTimestamp = currentTimestamp;
+ }
+
+ public long getNumNameTreeEntries() {
+ return numNameTreeEntries;
+ }
+
+ public void setNumNameTreeEntries(long numNameTreeEntries) {
+ this.numNameTreeEntries = numNameTreeEntries;
+ }
+
+ public long getNumFibEntries() {
+ return numFibEntries;
+ }
+
+ public void setNumFibEntries(long numFibEntries) {
+ this.numFibEntries = numFibEntries;
+ }
+
+ public long getNumPitEntries() {
+ return numPitEntries;
+ }
+
+ public void setNumPitEntries(long numPitEntries) {
+ this.numPitEntries = numPitEntries;
+ }
+
+ public long getNumMeasurementEntries() {
+ return numMeasurementEntries;
+ }
+
+ public void setNumMeasurementEntries(long numMeasurementEntries) {
+ this.numMeasurementEntries = numMeasurementEntries;
+ }
+
+ public long getNumCsEntries() {
+ return numCsEntries;
+ }
+
+ public void setNumCsEntries(long numCsEntries) {
+ this.numCsEntries = numCsEntries;
+ }
+
+ public long getNumInInterests() {
+ return numInInterests;
+ }
+
+ public void setNumInInterests(long numInInterests) {
+ this.numInInterests = numInInterests;
+ }
+
+ public long getNumInDatas() {
+ return numInDatas;
+ }
+
+ public void setNumInDatas(long numInDatas) {
+ this.numInDatas = numInDatas;
+ }
+
+ public long getNumInNacks() {
+ return numInNacks;
+ }
+
+ public void setNumInNacks(long numInNacks) {
+ this.numInNacks = numInNacks;
+ }
+
+ public long getNumOutInterests() {
+ return numOutInterests;
+ }
+
+ public void setNumOutInterests(long numOutInterests) {
+ this.numOutInterests = numOutInterests;
+ }
+
+ public long getNumOutDatas() {
+ return numOutDatas;
+ }
+
+ public void setNumOutDatas(long numOutDatas) {
+ this.numOutDatas = numOutDatas;
+ }
+
+ public long getNumOutNacks() {
+ return numOutNacks;
+ }
+
+ public void setNumOutNacks(long numOutNacks) {
+ this.numOutNacks = numOutNacks;
+ }
+
+ private String nfdVersion = "";
+ private long startTimestamp;
+ private long currentTimestamp;
+ private long numNameTreeEntries;
+ private long numFibEntries;
+ private long numPitEntries;
+ private long numMeasurementEntries;
+ private long numCsEntries;
+ private long numInInterests;
+ private long numInDatas;
+ private long numInNacks;
+ private long numOutInterests;
+ private long numOutDatas;
+ private long numOutNacks;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/LinkType.java b/app/src/main/java/com/intel/jndn/management/types/LinkType.java
new file mode 100644
index 0000000..a0f517c
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/LinkType.java
@@ -0,0 +1,35 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+/**
+ * Indicate the type of communication link; used by FaceStatus See
+ * <a href="http://redmine.named-data.net/projects/nfd/widi/FaceMgmt">http://redmine.named-data.net/projects/nfd/widi/FaceMgmt</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public enum LinkType {
+
+ POINT_TO_POINT(0),
+ MULTI_ACCESS(1);
+
+ LinkType(int value) {
+ value_ = value;
+ }
+
+ public final int getNumericValue() {
+ return value_;
+ }
+ private final int value_;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/LocalControlHeader.java b/app/src/main/java/com/intel/jndn/management/types/LocalControlHeader.java
new file mode 100644
index 0000000..5ca393f
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/LocalControlHeader.java
@@ -0,0 +1,36 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+/**
+ * Define constants for local control header options. See
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public enum LocalControlHeader {
+
+ INCOMING_FACE_ID(1),
+ NEXT_HOP_FACE_ID(2),
+ CACHING_POLICY(3);
+
+ LocalControlHeader(int value) {
+ value_ = value;
+ }
+
+ public final int getNumericValue() {
+ return value_;
+ }
+ private final int value_;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/NextHopRecord.java b/app/src/main/java/com/intel/jndn/management/types/NextHopRecord.java
new file mode 100644
index 0000000..3b40104
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/NextHopRecord.java
@@ -0,0 +1,119 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import java.nio.ByteBuffer;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.Tlv;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a NextHopRecord in a FibEntry; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NextHopRecord {
+
+ public final static int TLV_NEXT_HOP_RECORD = 129;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Cost, cost);
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId, faceId);
+ encoder.writeTypeAndLength(TLV_NEXT_HOP_RECORD, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws EncodingException For invalid encoding.
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ public final void wireDecode(TlvDecoder decoder) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_NEXT_HOP_RECORD);
+ this.faceId = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId);
+ this.cost = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Cost);
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get face ID
+ *
+ * @return
+ */
+ public int getFaceId() {
+ return faceId;
+ }
+
+ /**
+ * Set face ID
+ *
+ * @param faceId
+ */
+ public void setFaceId(int faceId) {
+ this.faceId = faceId;
+ }
+
+ /**
+ * Get cost
+ *
+ * @return
+ */
+ public int getCost() {
+ return cost;
+ }
+
+ /**
+ * Set cost
+ *
+ * @param cost
+ */
+ public void setCost(int cost) {
+ this.cost = cost;
+ }
+
+ private int faceId;
+ private int cost;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/RibEntry.java b/app/src/main/java/com/intel/jndn/management/types/RibEntry.java
new file mode 100644
index 0000000..705352c
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/RibEntry.java
@@ -0,0 +1,134 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.EncodingHelper;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a entry in the RIB; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset</a>
+ * for details
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class RibEntry implements Decodable {
+
+ /**
+ * TLV type, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments</a>
+ */
+ public final static int TLV_RIB_ENTRY = 128;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ for (Route route : routes) {
+ route.wireEncode(encoder);
+ }
+ EncodingHelper.encodeName(name, encoder);
+ encoder.writeTypeAndLength(TLV_RIB_ENTRY, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws EncodingException For invalid encoding.
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ @Override
+ public final void wireDecode(TlvDecoder decoder) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_RIB_ENTRY);
+ name = EncodingHelper.decodeName(decoder);
+ while (decoder.getOffset() < endOffset) {
+ Route route = new Route();
+ route.wireDecode(decoder);
+ routes.add(route);
+ }
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get name
+ *
+ * @return
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param name
+ */
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ /**
+ * Get routes
+ *
+ * @return
+ */
+ public List<Route> getRoutes() {
+ return routes;
+ }
+
+ /**
+ * Set routes
+ *
+ * @param routes
+ */
+ public void setRoutes(List<Route> routes) {
+ this.routes = routes;
+ }
+
+ private Name name = new Name();
+ private List<Route> routes = new ArrayList<>();
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/Route.java b/app/src/main/java/com/intel/jndn/management/types/Route.java
new file mode 100644
index 0000000..b4badf0
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/Route.java
@@ -0,0 +1,188 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import net.named_data.jndn.encoding.tlv.Tlv;
+import java.nio.ByteBuffer;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.encoding.tlv.TlvEncoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represent a Route object from /localhost/nfd/rib/list; see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset</a>
+ * for details.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class Route {
+
+ /**
+ * TLV type, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#TLV-TYPE-assignments</a>
+ */
+ public final static int TLV_ROUTE = 129;
+
+ /**
+ * Encode using a new TLV encoder.
+ *
+ * @return The encoded buffer.
+ */
+ public final Blob wireEncode() {
+ TlvEncoder encoder = new TlvEncoder();
+ wireEncode(encoder);
+ return new Blob(encoder.getOutput(), false);
+ }
+
+ /**
+ * Encode as part of an existing encode context.
+ *
+ * @param encoder
+ */
+ public final void wireEncode(TlvEncoder encoder) {
+ int saveLength = encoder.getLength();
+ encoder.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod, faceId);
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Flags, flags.getForwardingEntryFlags());
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Cost, cost);
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Origin, origin);
+ encoder.writeNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId, faceId);
+ encoder.writeTypeAndLength(TLV_ROUTE, encoder.getLength() - saveLength);
+ }
+
+ /**
+ * Decode the input from its TLV format.
+ *
+ * @param input The input buffer to decode. This reads from position() to
+ * limit(), but does not change the position.
+ * @throws net.named_data.jndn.encoding.EncodingException
+ */
+ public final void wireDecode(ByteBuffer input) throws EncodingException {
+ TlvDecoder decoder = new TlvDecoder(input);
+ wireDecode(decoder);
+ }
+
+ /**
+ * Decode as part of an existing decode context.
+ *
+ * @param decoder
+ * @throws EncodingException
+ */
+ public final void wireDecode(TlvDecoder decoder) throws EncodingException {
+ int endOffset = decoder.readNestedTlvsStart(TLV_ROUTE);
+ this.faceId = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId);
+ this.origin = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Origin);
+ this.cost = (int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Cost);
+ this.flags.setForwardingEntryFlags((int) decoder.readNonNegativeIntegerTlv(Tlv.ControlParameters_Flags));
+ this.expirationPeriod = (int) decoder.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod, endOffset);
+ decoder.finishNestedTlvs(endOffset);
+ }
+
+ /**
+ * Get Face ID
+ *
+ * @return
+ */
+ public int getFaceId() {
+ return faceId;
+ }
+
+ /**
+ * Set Face ID
+ *
+ * @param faceId
+ */
+ public void setFaceId(int faceId) {
+ this.faceId = faceId;
+ }
+
+ /**
+ * Get origin
+ *
+ * @return
+ */
+ public int getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Set origin
+ *
+ * @param origin
+ */
+ public void setOrigin(int origin) {
+ this.origin = origin;
+ }
+
+ /**
+ * Get cost
+ *
+ * @return
+ */
+ public int getCost() {
+ return cost;
+ }
+
+ /**
+ * Set cost
+ *
+ * @param cost
+ */
+ public void setCost(int cost) {
+ this.cost = cost;
+ }
+
+ /**
+ * Get flags
+ *
+ * @return
+ */
+ public ForwardingFlags getFlags() {
+ return flags;
+ }
+
+ /**
+ * Set flags
+ *
+ * @param flags
+ */
+ public void setFlags(ForwardingFlags flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Get expiration period
+ *
+ * @return
+ */
+ public double getExpirationPeriod() {
+ return expirationPeriod;
+ }
+
+ /**
+ * Set expiration period
+ *
+ * @param expirationPeriod
+ */
+ public void setExpirationPeriod(double expirationPeriod) {
+ this.expirationPeriod = expirationPeriod;
+ }
+
+ private int faceId = -1;
+ private int origin = -1;
+ private int cost = -1;
+ private ForwardingFlags flags = new ForwardingFlags();
+ private double expirationPeriod = -1.0;
+}
diff --git a/app/src/main/java/com/intel/jndn/management/types/StatusDataset.java b/app/src/main/java/com/intel/jndn/management/types/StatusDataset.java
new file mode 100644
index 0000000..0cc42c8
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/management/types/StatusDataset.java
@@ -0,0 +1,56 @@
+/*
+ * jndn-management
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.management.types;
+
+import com.intel.jndn.management.ManagementException;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.encoding.tlv.TlvDecoder;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Helper class to handle StatusDatasets, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class StatusDataset {
+
+ /**
+ * Decode multiple status entries as part of a StatusDataset, see
+ * <a href="http://redmine.named-data.net/projects/nfd/wiki/StatusDataset">http://redmine.named-data.net/projects/nfd/wiki/StatusDataset</a>
+ *
+ * @param <T>
+ * @param statusDataset
+ * @param type
+ * @return
+ * @throws com.intel.jndn.management.ManagementException
+ */
+ public static final <T extends Decodable> List<T> wireDecode(Blob statusDataset, Class<T> type) throws ManagementException {
+ List<T> entries = new ArrayList<>();
+ int endOffset = statusDataset.size();
+ TlvDecoder decoder = new TlvDecoder(statusDataset.buf());
+ while (decoder.getOffset() < endOffset) {
+ try {
+ T entry = type.newInstance();
+ entry.wireDecode(decoder);
+ entries.add(entry);
+ } catch (EncodingException | IllegalAccessException | InstantiationException e) {
+ throw new ManagementException("Failed to read status dataset.", e);
+ }
+ }
+ return entries;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/Client.java b/app/src/main/java/com/intel/jndn/utils/Client.java
new file mode 100644
index 0000000..6b09f9f
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/Client.java
@@ -0,0 +1,50 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+
+/**
+ * Base functionality provided by all NDN clients in this package.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface Client {
+
+ /**
+ * Asynchronously request the Data for an Interest. This will send the
+ * Interest and return immediately; use futureData.get() to block until the
+ * Data returns (see Future) or manage the event processing independently.
+ *
+ * @param face
+ * @param interest
+ * @return
+ */
+ public Future<Data> getAsync(Face face, Interest interest);
+
+ /**
+ * Synchronously retrieve the Data for an Interest; this will block until
+ * complete (i.e. either data is received or the interest times out).
+ *
+ * @param face
+ * @param interest
+ * @return
+ * @throws java.io.IOException
+ */
+ public Data getSync(Face face, Interest interest) throws IOException;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/DynamicServer.java b/app/src/main/java/com/intel/jndn/utils/DynamicServer.java
new file mode 100644
index 0000000..8206e27
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/DynamicServer.java
@@ -0,0 +1,40 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.server.RespondWithData;
+import com.intel.jndn.utils.server.Server;
+import java.io.IOException;
+
+/**
+ * Defines the API for a {@link Server} producing {@link Data} packets
+ * dynamically; in other words, when an {@link Interest} arrives, this server
+ * will run a callback to determine what packet to send back. As good practice,
+ * keep callback methods as short as possible.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface DynamicServer extends Server {
+
+ /**
+ * Set the callback method to run when an {@link Interest} packet is passed to
+ * this server. This method should either return a {@link Data} packet that
+ * satisfies the Interest or throw an Exception to avoid sending. Calling this
+ * method a second time should replace the callback.
+ *
+ * @param callback the callback instance
+ * @throws java.io.IOException if the server fails to register a prefix
+ */
+ public void respondUsing(RespondWithData callback) throws IOException;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/InternalFace.java b/app/src/main/java/com/intel/jndn/utils/InternalFace.java
new file mode 100644
index 0000000..2850363
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/InternalFace.java
@@ -0,0 +1,23 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+/**
+ * TODO waiting on Face to become overridable
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class InternalFace {
+
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/RepositoryServer.java b/app/src/main/java/com/intel/jndn/utils/RepositoryServer.java
new file mode 100644
index 0000000..0364eb7
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/RepositoryServer.java
@@ -0,0 +1,38 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.server.Server;
+import java.io.IOException;
+import net.named_data.jndn.Data;
+
+/**
+ * Defines the API for a {@link Server} producing {@link Data} packets and
+ * storing them until they are requested; this server corresponds closely to use
+ * cases such as: cache, file system, web server.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+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.
+ *
+ * @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;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/SegmentedClient.java b/app/src/main/java/com/intel/jndn/utils/SegmentedClient.java
new file mode 100644
index 0000000..2879a29
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/SegmentedClient.java
@@ -0,0 +1,211 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.client.SegmentedFutureData;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+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.encoding.EncodingException;
+
+import net.named_data.nfd.utils.G;
+
+/**
+ * Provide a client to simplify retrieving segmented Data packets over the NDN
+ * network. This class expects the Data producer to follow the NDN naming
+ * conventions (see http://named-data.net/doc/tech-memos/naming-conventions.pdf)
+ * and produce Data packets with a valid segment as the last component of their
+ * name; additionally, at least the first packet should set the FinalBlockId of
+ * the packet's MetaInfo (see
+ * http://named-data.net/doc/ndn-tlv/data.html#finalblockid).
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SegmentedClient implements Client {
+
+ private static SegmentedClient defaultInstance;
+ private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
+
+ /**
+ * Singleton access for simpler client use.
+ *
+ * @return
+ */
+ public static SegmentedClient getDefault() {
+ if (defaultInstance == null) {
+ defaultInstance = new SegmentedClient();
+ }
+ return defaultInstance;
+ }
+
+ /**
+ * Asynchronously send Interest packets for a segmented result; will block
+ * until the first packet is received and then send remaining interests until
+ * the specified FinalBlockId.
+ *
+ * @param face
+ * @param interest should include either a ChildSelector or an initial segment
+ * number; the initial segment number will be cut off in the de-segmented
+ * packet.
+ * @return a list of FutureData packets; if the first segment fails, the list
+ * will contain one FutureData with the failure exception
+ */
+ @Override
+ public Future<Data> getAsync(Face face, Interest interest) {
+ List<Future<Data>> segments = getAsyncList(face, interest);
+ Name name = hasSegment(interest.getName()) ? interest.getName().getPrefix(-1) : interest.getName();
+ return new SegmentedFutureData(name, segments);
+ }
+
+ /**
+ * Asynchronously send Interest packets for a segmented result; will block
+ * until the first packet is received and then send remaining interests until
+ * the specified FinalBlockId.
+ *
+ * @param face
+ * @param name the {@link Name} of the packet to retrieve using a default
+ * interest
+ * @return an aggregated data packet from all received segments
+ */
+ public Future<Data> getAsync(Face face, Name name) {
+ return getAsync(face, SimpleClient.getDefaultInterest(name));
+ }
+
+ /**
+ * Asynchronously send Interest packets for a segmented result; will block
+ * until the first packet is received and then send remaining interests until
+ * the specified FinalBlockId.
+ *
+ * @param face
+ * @param interest should include either a ChildSelector or an initial segment
+ * number
+ * @return a list of FutureData packets; if the first segment fails, the list
+ * will contain one FutureData with the failure exception
+ */
+ public List<Future<Data>> getAsyncList(Face face, Interest interest) {
+ // get first segment; default 0 or use a specified start segment
+ long firstSegment = 0;
+ boolean specifiedSegment = false;
+ try {
+ firstSegment = interest.getName().get(-1).toSegment();
+ specifiedSegment = true;
+ } catch (EncodingException e) {
+ // check for interest selector if no initial segment found
+ if (interest.getChildSelector() == -1) {
+ logger.log(Level.WARNING, "No child selector set for a segmented Interest; this may result in incorrect retrieval.");
+ // allow this interest to pass without a segment marker since it may still succeed
+ }
+ }
+
+ // setup segments
+ final List<Future<Data>> segments = new ArrayList<>();
+ segments.add(SimpleClient.getDefault().getAsync(face, interest));
+
+ // retrieve first packet to find last segment value
+ long lastSegment;
+ try {
+ G.Log("+++++++ " + segments.get(0).get().getMetaInfo().getFinalBlockId().toEscapedString());
+ lastSegment = segments.get(0).get().getMetaInfo().getFinalBlockId().toSegment();
+ } catch (ExecutionException | InterruptedException | EncodingException e) {
+ logger.log(Level.SEVERE, "Failed to retrieve first segment: ", e);
+ return segments;
+ }
+
+ // cut interest segment off
+ if (specifiedSegment) {
+ interest.setName(interest.getName().getPrefix(-1));
+ }
+
+ // send interests in remaining segments
+ for (long i = firstSegment + 1; i <= lastSegment; i++) {
+ Interest segmentedInterest = new Interest(interest);
+ segmentedInterest.getName().appendSegment(i);
+ Future<Data> futureData = SimpleClient.getDefault().getAsync(face, segmentedInterest);
+ segments.add((int) i, futureData);
+ }
+
+ return segments;
+ }
+
+ /**
+ * Asynchronously send Interests for a segmented Data packet using a default
+ * interest (e.g. 2 second timeout); this will block until complete (i.e.
+ * either data is received or the interest times out). See getAsync(Face face)
+ * for more information.
+ *
+ * @param face
+ * @param name
+ * @return
+ */
+ public List<Future<Data>> getAsyncList(Face face, Name name) {
+ return getAsyncList(face, SimpleClient.getDefaultInterest(name));
+ }
+
+ /**
+ * Retrieve a segmented Data packet; will block until all segments are
+ * received and will re-assemble these.
+ *
+ * @param face
+ * @param interest should include either a ChildSelector or an initial segment
+ * number
+ * @return a Data packet; the name will inherit from the sent Interest, not
+ * the returned packets and the content will be a concatenation of all of the
+ * packet contents.
+ * @throws java.io.IOException
+ */
+ @Override
+ public Data getSync(Face face, Interest interest) throws IOException {
+ try {
+ return getAsync(face, interest).get();
+ } catch (ExecutionException | InterruptedException e) {
+ logger.log(Level.WARNING, "Failed to retrieve data.", e);
+ throw new IOException("Failed to retrieve data.", e);
+ }
+ }
+
+ /**
+ * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
+ * second timeout); this will block until complete (i.e. either data is
+ * received or the interest times out). See getSync(Face face) for more
+ * information.
+ *
+ * @param face
+ * @param name
+ * @return
+ * @throws java.io.IOException
+ */
+ public Data getSync(Face face, Name name) throws IOException {
+ return getSync(face, SimpleClient.getDefaultInterest(name));
+ }
+
+ /**
+ * Check if a name ends in a segment component; uses marker value found in the
+ * NDN naming conventions (see
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
+ *
+ * @param name
+ * @return
+ */
+ public static boolean hasSegment(Name name) {
+ return name.get(-1).getValue().buf().get(0) == 0x00;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/SegmentedServer.java b/app/src/main/java/com/intel/jndn/utils/SegmentedServer.java
new file mode 100644
index 0000000..0daf0af
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/SegmentedServer.java
@@ -0,0 +1,91 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.repository.ForLoopRepository;
+import com.intel.jndn.utils.repository.Repository;
+import com.intel.jndn.utils.server.SegmentedServerHelper;
+import com.intel.jndn.utils.server.ServerBaseImpl;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+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.encoding.EncodingException;
+import net.named_data.jndn.transport.Transport;
+
+/**
+ * Implementation of a {@link RepositoryServer} that segments packets stored in
+ * its repository.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SegmentedServer extends ServerBaseImpl implements RepositoryServer {
+
+ private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
+ private final Repository repository = new ForLoopRepository();
+
+ /**
+ * {@inheritDoc}
+ */
+ public SegmentedServer(Face face, Name prefix) {
+ super(face, prefix);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void serve(Data data) throws IOException {
+ if (!isRegistered()) {
+ 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);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
+ if (interest.getChildSelector() == -1) {
+ try {
+ interest.getName().get(-1).toSegment();
+ } catch (EncodingException e) {
+ interest.setChildSelector(Interest.CHILD_SELECTOR_LEFT);
+ }
+ }
+
+ try {
+ Data data = repository.get(interest);
+ data = processPipeline(data);
+ ByteBuffer buffer = data.wireEncode().buf();
+ transport.send(buffer);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to send data for: " + interest.toUri(), e);
+ }
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/SimpleClient.java b/app/src/main/java/com/intel/jndn/utils/SimpleClient.java
new file mode 100644
index 0000000..a751f8e
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/SimpleClient.java
@@ -0,0 +1,145 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.client.FutureData;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+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 java.util.logging.Logger;
+
+/**
+ * Provide a client to simplify information retrieval over the NDN network.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SimpleClient implements Client {
+
+ public static final long DEFAULT_SLEEP_TIME = 20;
+ public static final long DEFAULT_TIMEOUT = 2000;
+ private static final Logger logger = Logger.getLogger(SimpleClient.class.getName());
+ private static SimpleClient defaultInstance;
+
+ /**
+ * Singleton access for simpler client use
+ *
+ * @return
+ */
+ public static SimpleClient getDefault() {
+ if (defaultInstance == null) {
+ defaultInstance = new SimpleClient();
+ }
+ return defaultInstance;
+ }
+
+ /**
+ * Asynchronously request the Data for an Interest. This will send the
+ * Interest and return immediately; use futureData.get() to block until the
+ * Data returns (see FutureData) or manage the event processing independently.
+ *
+ * @param face
+ * @param interest
+ * @return
+ */
+ @Override
+ public Future<Data> getAsync(Face face, Interest interest) {
+ final FutureData futureData = new FutureData(face, interest.getName());
+
+ // send interest
+ try {
+ face.expressInterest(interest, new OnData() {
+ @Override
+ public void onData(Interest interest, Data data) {
+ futureData.resolve(data);
+ }
+ }, new OnTimeout() {
+ @Override
+ public void onTimeout(Interest interest) {
+ futureData.reject(new TimeoutException());
+ }
+ });
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "IO failure while sending interest: ", e);
+ futureData.reject(e);
+ }
+
+ return futureData;
+ }
+
+ /**
+ * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
+ * second timeout); this will block until complete (i.e. either data is
+ * received or the interest times out).
+ *
+ * @param face
+ * @param name
+ * @return
+ */
+ public Future<Data> getAsync(Face face, Name name) {
+ return getAsync(face, getDefaultInterest(name));
+ }
+
+ /**
+ * Synchronously retrieve the Data for an Interest; this will block until
+ * complete (i.e. either data is received or the interest times out).
+ *
+ * @param face
+ * @param interest
+ * @return Data packet or null
+ * @throws java.io.IOException
+ */
+ @Override
+ public Data getSync(Face face, Interest interest) throws IOException {
+ try {
+ return getAsync(face, interest).get();
+ } catch (ExecutionException | InterruptedException e) {
+ logger.log(Level.WARNING, "Failed to retrieve data.", e);
+ throw new IOException("Failed to retrieve data.", e);
+ }
+ }
+
+ /**
+ * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
+ * second timeout); this will block until complete (i.e. either data is
+ * received or the interest times out).
+ *
+ * @param face
+ * @param name
+ * @return
+ * @throws java.io.IOException
+ */
+ public Data getSync(Face face, Name name) throws IOException {
+ return getSync(face, getDefaultInterest(name));
+ }
+
+ /**
+ * Create a default interest for a given Name using some common settings: -
+ * lifetime: 2 seconds
+ *
+ * @param name
+ * @return
+ */
+ public static Interest getDefaultInterest(Name name) {
+ Interest interest = new Interest(name, DEFAULT_TIMEOUT);
+ return interest;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/SimpleServer.java b/app/src/main/java/com/intel/jndn/utils/SimpleServer.java
new file mode 100644
index 0000000..0bd4667
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/SimpleServer.java
@@ -0,0 +1,96 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils;
+
+import com.intel.jndn.utils.server.RespondWithData;
+import com.intel.jndn.utils.server.RespondWithBlob;
+import com.intel.jndn.utils.server.ServerBaseImpl;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+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.transport.Transport;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Implementation of a {@link DynamicServer} that wraps the {@link OnInterest}
+ * callback with some encoding and pipeline support.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SimpleServer extends ServerBaseImpl implements DynamicServer {
+
+ private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
+ private RespondWithData callback;
+
+ /**
+ * {@inheritDoc}
+ */
+ public SimpleServer(Face face, Name prefix) {
+ super(face, prefix);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void respondUsing(RespondWithData callback) throws IOException {
+ if (!isRegistered()) {
+ register();
+ }
+ this.callback = callback;
+ }
+
+ /**
+ * Convenience method for responding to an {@link Interest} by returning the
+ * {@link Blob} content only; when an Interest arrives, this method wraps the
+ * returned Blob with a {@link Data} using the exact {@link Name} of the
+ * incoming Interest.
+ *
+ * @param callback the callback function to retrieve content when an
+ * {@link Interest} arrives
+ * @throws java.io.IOException if the server fails to register a prefix
+ */
+ public void respondUsing(final RespondWithBlob callback) throws IOException {
+ RespondWithData dataCallback = new RespondWithData() {
+ @Override
+ public Data onInterest(Name prefix, Interest interest) throws Exception {
+ Data data = new Data(interest.getName());
+ Blob content = callback.onInterest(prefix, interest);
+ data.setContent(content);
+ return data;
+ }
+ };
+ respondUsing(dataCallback);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
+ try {
+ Data data = callback.onInterest(prefix, interest);
+ data = processPipeline(data);
+ ByteBuffer buffer = data.wireEncode().buf();
+ transport.send(buffer);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to send data for: " + interest.toUri(), e);
+ }
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/client/FutureData.java b/app/src/main/java/com/intel/jndn/utils/client/FutureData.java
new file mode 100644
index 0000000..dc6b393
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/client/FutureData.java
@@ -0,0 +1,197 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.client;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+
+/**
+ * Reference to a Packet that has yet to be returned from the network. Usage:
+ *
+ * <pre><code>
+ * FutureData futureData = new FutureData(face, interest.getName());
+ * face.expressInterest(interest, new OnData(){
+ * ... futureData.resolve(data); ...
+ * }, new OnTimeout(){
+ * ... futureData.reject(new TimeoutException());
+ * });
+ * Data resolvedData = futureData.get(); // will block and call face.processEvents() until complete
+ * </code></pre>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class FutureData implements Future<Data> {
+
+ protected final Face face;
+ private final Name name;
+ private Data data;
+ private boolean cancelled = false;
+ private Throwable error;
+
+ /**
+ * Constructor
+ *
+ * @param face
+ * @param name
+ */
+ public FutureData(Face face, Name name) {
+ this.face = face;
+ this.name = new Name(name);
+ }
+
+ /**
+ * Get the Interest name.
+ *
+ * @return
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Cancel the current request.
+ *
+ * @param mayInterruptIfRunning
+ * @return
+ */
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ cancelled = true;
+ return cancelled;
+ }
+
+ /**
+ * Determine if this request is cancelled.
+ *
+ * @return
+ */
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ /**
+ * Determine if the request has completed (successfully or not).
+ *
+ * @return
+ */
+ @Override
+ public boolean isDone() {
+ return data != null || error != null || isCancelled();
+ }
+
+ /**
+ * Set the packet when successfully retrieved; unblocks get().
+ *
+ * @param d
+ */
+ public void resolve(Data d) {
+ data = d;
+ }
+
+ /**
+ * Set the exception when request failed; unblocks get().
+ *
+ * @param e
+ */
+ public void reject(Throwable e) {
+ error = e;
+ }
+
+ /**
+ * Block until packet is retrieved.
+ *
+ * @return
+ * @throws InterruptedException
+ * @throws ExecutionException
+ */
+ @Override
+ public Data get() throws InterruptedException, ExecutionException {
+ while (!isDone()) {
+ // process face events
+ try {
+ synchronized (face) {
+ face.processEvents();
+ }
+ } catch (EncodingException | IOException e) {
+ throw new ExecutionException("Failed to retrieve packet.", e);
+ }
+ }
+
+ // case: cancelled
+ if (cancelled) {
+ throw new InterruptedException("Interrupted by user.");
+ }
+
+ // case: error
+ if (error != null) {
+ throw new ExecutionException("Future rejected with error.", error);
+ }
+
+ return data;
+ }
+
+ /**
+ * Block until packet is retrieved or timeout is reached.
+ *
+ * @param timeout
+ * @param unit
+ * @return
+ * @throws InterruptedException
+ * @throws ExecutionException
+ * @throws TimeoutException
+ */
+ @Override
+ public Data get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ long interval = TimeUnit.MILLISECONDS.convert(timeout, unit);
+ long endTime = System.currentTimeMillis() + interval;
+ long currentTime = System.currentTimeMillis();
+ while (!isDone() && !isCancelled() && currentTime <= endTime) {
+ // process face events
+ try {
+ synchronized (face) {
+ face.processEvents();
+ }
+ } catch (EncodingException | IOException e) {
+ throw new ExecutionException("Failed to retrieve packet.", e);
+ }
+
+ currentTime = System.currentTimeMillis();
+ }
+
+ // case: cancelled
+ if (cancelled) {
+ throw new InterruptedException("Interrupted by user.");
+ }
+
+ // case: error
+ if (error != null) {
+ throw new ExecutionException("Future rejected with error.", error);
+ }
+
+ // case: timed out
+ if (currentTime > endTime) {
+ throw new TimeoutException("Timed out.");
+ }
+
+ return data;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/client/SegmentedFutureData.java b/app/src/main/java/com/intel/jndn/utils/client/SegmentedFutureData.java
new file mode 100644
index 0000000..dec33df
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/client/SegmentedFutureData.java
@@ -0,0 +1,172 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.client;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Represents a list of Packets that have been requested asynchronously and have
+ * yet to be returned from the network. Usage:
+ *
+ * <pre><code>
+ * SegmentedFutureData segmentedFutureData = new SegmentedFutureData(face, name, futureDataList);
+ * Data data = segmentedFutureData.get(); // will block until complete
+ * </code></pre>
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SegmentedFutureData implements Future<Data> {
+
+ private final Name name;
+ List<Future<Data>> segments;
+ private boolean cancelled = false;
+
+ /**
+ * Constructor
+ *
+ * @param name this will be the name of the returned Data packet, regardless
+ * of suffixes (e.g. segment components) on each segment packet
+ * @param segments
+ */
+ public SegmentedFutureData(Name name, List<Future<Data>> segments) {
+ this.name = name;
+ this.segments = segments;
+ }
+
+ /**
+ * Get the Interest name; this will also be the name of the Data packet
+ * returned from get().
+ *
+ * @return
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Cancel the current request.
+ *
+ * @param mayInterruptIfRunning
+ * @return
+ */
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ cancelled = true;
+ return cancelled;
+ }
+
+ /**
+ * Determine if this request is cancelled.
+ *
+ * @return
+ */
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ /**
+ * Determine if the request has completed (successfully or not).
+ *
+ * @return
+ */
+ @Override
+ public boolean isDone() {
+ // check for errors, cancellation
+ if (isCancelled()) {
+ return true;
+ }
+
+ // check each segment for completion
+ for (Future<Data> futureData : segments) {
+ if (!futureData.isDone()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Block until packet is retrieved.
+ *
+ * @return
+ * @throws InterruptedException
+ * @throws ExecutionException
+ */
+ @Override
+ public Data get() throws InterruptedException, ExecutionException {
+ // aggregate bytes
+ ByteArrayOutputStream content = new ByteArrayOutputStream();
+ for (Future<Data> futureData : segments) {
+ try {
+ content.write(futureData.get().getContent().getImmutableArray());
+ } catch (ExecutionException | IOException | InterruptedException e) {
+ throw new ExecutionException("Failed while aggregating retrieved packets.", e);
+ }
+ }
+
+ // build aggregated packet (copy first packet)
+ Data data = new Data(segments.get(0).get());
+ data.setName(getName());
+ data.setContent(new Blob(content.toByteArray()));
+ return data;
+ }
+
+ /**
+ * Block until packet is retrieved or timeout is reached.
+ *
+ * @param timeout
+ * @param unit
+ * @return
+ * @throws InterruptedException
+ * @throws ExecutionException
+ * @throws TimeoutException
+ */
+ @Override
+ public Data get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ long interval = TimeUnit.MILLISECONDS.convert(timeout, unit);
+ long endTime = System.currentTimeMillis() + interval;
+
+ // aggregate bytes
+ ByteArrayOutputStream content = new ByteArrayOutputStream();
+ for (Future<Data> futureData : segments) {
+ try {
+ content.write(futureData.get().getContent().getImmutableArray());
+ } catch (ExecutionException | IOException | InterruptedException e) {
+ throw new ExecutionException("Failed while aggregating retrieved packets.", e);
+ }
+
+ // check for timeout
+ if (System.currentTimeMillis() > endTime) {
+ throw new TimeoutException("Timed out.");
+ }
+ }
+
+ // build aggregated packet (copy first packet)
+ Data data = new Data(segments.get(0).get());
+ data.setName(getName());
+ data.setContent(new Blob(content.toByteArray()));
+ return data;
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/repository/DataNotFoundException.java b/app/src/main/java/com/intel/jndn/utils/repository/DataNotFoundException.java
new file mode 100644
index 0000000..732cc9c
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/repository/DataNotFoundException.java
@@ -0,0 +1,23 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.repository;
+
+/**
+ * Thrown when a {@link Repository} cannot retrieve a stored packet.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class DataNotFoundException extends Exception {
+
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java b/app/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java
new file mode 100644
index 0000000..cb43655
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/repository/ForLoopRepository.java
@@ -0,0 +1,110 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+/**
+ * Store {@link Data} packets in a linked list and iterate over the list to find
+ * the best match; this is a subset of the functionality provided in
+ * {@link net.named_data.jndn.util.MemoryContentCache} and borrows the matching
+ * logic from there. Code for removing stale packets is not yet implemented.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class ForLoopRepository implements Repository {
+
+ private List<Data> storage = new ArrayList<>();
+
+ /**
+ * Helper data structure
+ */
+ private class Record {
+
+ public Name name;
+ public Data data;
+
+ public Record(Name name, Data data) {
+ this.name = name;
+ this.data = data;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(Data data) {
+ storage.add(data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Data get(Interest interest) throws DataNotFoundException {
+ Name.Component selectedComponent = null;
+ Data selectedData = null;
+ for (Data content : storage) {
+ if (interest.matchesName(content.getName())) {
+ if (interest.getChildSelector() < 0) {
+ // No child selector, so send the first match that we have found.
+ return content;
+ } else {
+ // Update selectedEncoding based on the child selector.
+ Name.Component component;
+ if (content.getName().size() > interest.getName().size()) {
+ component = content.getName().get(interest.getName().size());
+ } else {
+ component = new Name.Component();
+ }
+
+ boolean gotBetterMatch = false;
+ if (selectedData == null) {
+ // Save the first match.
+ gotBetterMatch = true;
+ } else {
+ if (interest.getChildSelector() == 0) {
+ // Leftmost child.
+ if (component.compare(selectedComponent) < 0) {
+ gotBetterMatch = true;
+ }
+ } else {
+ // Rightmost child.
+ if (component.compare(selectedComponent) > 0) {
+ gotBetterMatch = true;
+ }
+ }
+ }
+
+ if (gotBetterMatch) {
+ selectedComponent = component;
+ selectedData = content;
+ }
+ }
+ }
+ }
+
+ if (selectedData != null) {
+ // We found the leftmost or rightmost child.
+ return selectedData;
+ } else {
+ throw new DataNotFoundException();
+ }
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/repository/NavigableTreeRepository.java b/app/src/main/java/com/intel/jndn/utils/repository/NavigableTreeRepository.java
new file mode 100644
index 0000000..ec47190
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/repository/NavigableTreeRepository.java
@@ -0,0 +1,146 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.repository;
+
+import java.util.NavigableMap;
+import java.util.TreeMap;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+/**
+ * Store {@link Data} packets in a {@link TreeMap}; this is an initial concept
+ * class and should not be used in production. In tests, see
+ * RepositoryTest.java, this class is faster on retrieval but not enough to make
+ * up for its slow put().
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class NavigableTreeRepository implements Repository {
+
+ private NavigableMap<Name, PossibleData> storage = new TreeMap<>();
+
+ /**
+ * Helper data structure
+ */
+ private class PossibleData {
+
+ public PossibleData() {
+ // no data provided
+ }
+
+ public PossibleData(Data data) {
+ this.data = data;
+ }
+ public Data data;
+
+ public boolean hasData() {
+ return data != null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void put(Data data) {
+ storage.put(data.getName(), new PossibleData(data));
+
+ Name name = data.getName();
+ do {
+ name = name.getPrefix(-1);
+ if (storage.get(name) == null) {
+ storage.put(name, new PossibleData());
+ }
+ } while (name.size() > 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Data get(Interest interest) throws DataNotFoundException {
+ Data data;
+ if (interest.getChildSelector() == Interest.CHILD_SELECTOR_LEFT) {
+ data = getLowest(interest);
+ } else if (interest.getChildSelector() == Interest.CHILD_SELECTOR_RIGHT) {
+ data = getHighest(interest);
+ } else {
+ data = getFirstMatching(interest);
+ }
+ checkForNull(data);
+ return data;
+ }
+
+ /**
+ * @param interest the {@link Interest} to search with
+ * @return the lowest matching {@link Data} packet
+ */
+ private Data getLowest(Interest interest) {
+ PossibleData found = storage.get(interest.getName());
+
+ Name name = interest.getName();
+ while (found != null && interest.matchesName(name)) {
+ name = storage.lowerKey(name);
+ found = (name != null) ? storage.get(name) : null;
+ }
+
+ return found == null ? null : found.data;
+ }
+
+ /**
+ * @param interest the {@link Interest} to search with
+ * @return the highest matching {@link Data} packet
+ */
+ private Data getHighest(Interest interest) {
+ PossibleData found = storage.get(interest.getName());
+
+ if (found != null) {
+ Name name = interest.getName();
+ while (name != null && interest.matchesName(name)) {
+ found = storage.get(name);
+ name = storage.higherKey(name);
+ }
+ }
+
+ return found == null ? null : found.data;
+ }
+
+ /**
+ * @param interest the {@link Interest} to search with
+ * @return the first matching {@link Data} packet
+ */
+ private Data getFirstMatching(Interest interest) {
+ PossibleData found = storage.get(interest.getName());
+
+ Name name = interest.getName();
+ while (found != null && !found.hasData() && interest.matchesName(name)) {
+ name = storage.higherKey(name);
+ found = (name != null) ? storage.get(name) : null;
+ }
+
+ return found == null ? null : found.data;
+ }
+
+ /**
+ * @param data the {@link Data} packet to check
+ * @throws DataNotFoundException if data is null
+ */
+ private void checkForNull(Data data) throws DataNotFoundException {
+ if (data == null) {
+ throw new DataNotFoundException();
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/repository/Repository.java b/app/src/main/java/com/intel/jndn/utils/repository/Repository.java
new file mode 100644
index 0000000..9444e24
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/repository/Repository.java
@@ -0,0 +1,43 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.repository;
+
+import com.intel.jndn.utils.repository.DataNotFoundException;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+
+/**
+ * Define API for storing and retrieving NDN packets
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface Repository {
+
+ /**
+ * Put a {@link Data} packet in the repository.
+ *
+ * @param data a {@link Data} packet
+ */
+ public void put(Data data);
+
+ /**
+ * Retrieve a {@link Data} packet in the repository; this method should
+ * respect child selectors, exclude selectors, etc.
+ *
+ * @param interest the {@link Interest}
+ * @return a {@link Data} packet
+ * @throws DataNotFoundException if the packet is not found
+ */
+ public Data get(Interest interest) throws DataNotFoundException;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/PipelineStage.java b/app/src/main/java/com/intel/jndn/utils/server/PipelineStage.java
new file mode 100644
index 0000000..15d23f0
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/PipelineStage.java
@@ -0,0 +1,24 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+/**
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface PipelineStage<T, Y> {
+ public Y process(T context) throws Exception;
+// public void setNextStage(PipelineStage<Y, ?> nextStage);
+// public PipelineStage<Y, ?> getNextStage();
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/RespondWithBlob.java b/app/src/main/java/com/intel/jndn/utils/server/RespondWithBlob.java
new file mode 100644
index 0000000..fe7f74a
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/RespondWithBlob.java
@@ -0,0 +1,28 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Functional interface for serving data from Server.on()
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface RespondWithBlob {
+
+ public Blob onInterest(Name prefix, Interest interest) throws Exception;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/RespondWithData.java b/app/src/main/java/com/intel/jndn/utils/server/RespondWithData.java
new file mode 100644
index 0000000..95d0e6a
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/RespondWithData.java
@@ -0,0 +1,28 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+
+/**
+ * Functional interface for serving data from Server.on()
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface RespondWithData {
+
+ public Data onInterest(Name prefix, Interest interest) throws Exception;
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/SegmentedServerHelper.java b/app/src/main/java/com/intel/jndn/utils/server/SegmentedServerHelper.java
new file mode 100644
index 0000000..2e1f92c
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/SegmentedServerHelper.java
@@ -0,0 +1,102 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Helper for segmenting an input stream into a list of Data packets. Current
+ * use of the default segment size of 4096 (only for
+ * {@link #segment(net.named_data.jndn.Data, java.io.InputStream)} is based on
+ * several assumptions: NDN packet size was limited to 8000 at the time this was
+ * written and signature size is unknown.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SegmentedServerHelper {
+
+ public static final int DEFAULT_SEGMENT_SIZE = 4096;
+
+ /**
+ * Segment a stream of bytes into a list of Data packets; this must read all
+ * the bytes first in order to determine the end segment for FinalBlockId.
+ *
+ * @param template the {@link Data} packet to use for the segment {@link Name},
+ * {@link net.named_data.jndn.MetaInfo}, etc.
+ * @param bytes an {@link InputStream} to the bytes to segment
+ * @return a list of segmented {@link Data} packets
+ * @throws IOException if the stream fails
+ */
+ public static List<Data> segment(Data template, InputStream bytes) throws IOException {
+ return segment(template, bytes, DEFAULT_SEGMENT_SIZE);
+ }
+
+ /**
+ * Segment a stream of bytes into a list of Data packets; this must read all
+ * the bytes first in order to determine the end segment for FinalBlockId.
+ *
+ * @param template the {@link Data} packet to use for the segment {@link Name},
+ * {@link net.named_data.jndn.MetaInfo}, etc.
+ * @param bytes an {@link InputStream} to the bytes to segment
+ * @return a list of segmented {@link Data} packets
+ * @throws IOException if the stream fails
+ */
+ public static List<Data> segment(Data template, InputStream bytes, int segmentSize) throws IOException {
+ List<Data> segments = new ArrayList<>();
+ byte[] buffer_ = readAll(bytes);
+ int numBytes = buffer_.length;
+ int numPackets = (int) Math.ceil((double) numBytes / segmentSize);
+ ByteBuffer buffer = ByteBuffer.wrap(buffer_, 0, numBytes);
+ Name.Component lastSegment = Name.Component.fromNumberWithMarker(numPackets - 1, 0x00);
+
+ for (int i = 0; i < numPackets; i++) {
+ Data segment = new Data(template);
+ segment.getName().appendSegment(i);
+ segment.getMetaInfo().setFinalBlockId(lastSegment);
+ byte[] content = new byte[Math.min(segmentSize, buffer.remaining())];
+ buffer.get(content);
+ segment.setContent(new Blob(content));
+ segments.add(segment);
+ }
+
+ return segments;
+ }
+
+ /**
+ * Read all of the bytes in an input stream.
+ *
+ * @param bytes the {@link InputStream} of bytes to read
+ * @return an array of all bytes retrieved from the stream
+ * @throws IOException if the stream fails
+ */
+ public static byte[] readAll(InputStream bytes) throws IOException {
+ ByteArrayOutputStream builder = new ByteArrayOutputStream();
+ int read = bytes.read();
+ while (read != -1) {
+ builder.write(read);
+ read = bytes.read();
+ }
+ builder.flush();
+ bytes.close();
+ return builder.toByteArray();
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/Server.java b/app/src/main/java/com/intel/jndn/utils/server/Server.java
new file mode 100644
index 0000000..ba4ff1c
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/Server.java
@@ -0,0 +1,46 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnInterest;
+
+/**
+ * Base interface for defining a server; see descendant interfaces for different
+ * modes of serving packets. This class extends {@link Runnable} expecting
+ * implementing classes to do any necessary event processing in the
+ * {@link Runnable#run()} block, thus allowing different ways to manage servers
+ * (e.g. single-thread vs {@link ScheduledThreadPoolExecutor}.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public interface Server extends Runnable, OnInterest {
+
+ /**
+ * @return the {@link Name} prefix this server is serving on.
+ */
+ public Name getPrefix();
+
+ /**
+ * Add a stage to the server pipeline. Each stage should be processed once the
+ * server has the {@link Data} packet available to send (e.g. after a callback
+ * has produced a packet); also, stages should be processed in the order they
+ * are added.
+ *
+ * @param pipelineStage a Data-to-Data processing stage
+ */
+ public void addPipelineStage(PipelineStage<Data, Data> pipelineStage);
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/ServerBaseImpl.java b/app/src/main/java/com/intel/jndn/utils/server/ServerBaseImpl.java
new file mode 100644
index 0000000..0d150d6
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/ServerBaseImpl.java
@@ -0,0 +1,140 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnRegisterFailed;
+import net.named_data.jndn.encoding.EncodingException;
+
+/**
+ * Base implementation for a {@link Server}.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public abstract class ServerBaseImpl implements Server {
+
+ private static final Logger logger = Logger.getLogger(ServerBaseImpl.class.getName());
+ private final Face face;
+ private final Name prefix;
+ private final List<PipelineStage> pipeline = new ArrayList<>();
+ private boolean registered = false;
+
+ /**
+ * Build the base server; register() must run separately and is run
+ * automatically on {@link #run()}.
+ *
+ * @param face a {@link Face} allowing prefix registration (see
+ * {@link Face#setCommandSigningInfo(net.named_data.jndn.security.KeyChain, net.named_data.jndn.Name)}
+ * @param prefix the {@link Name} to register
+ */
+ public ServerBaseImpl(Face face, Name prefix) {
+ this.face = face;
+ this.prefix = prefix;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Name getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * @return true if the server has registered a prefix on the face
+ */
+ public boolean isRegistered() {
+ return registered;
+ }
+
+ /**
+ * Register a prefix for responding to interests.
+ *
+ * @throws java.io.IOException if IO fails
+ */
+ public void register() throws IOException {
+ registered = true;
+ try {
+ face.registerPrefix(prefix, this, new OnRegisterFailed() {
+ @Override
+ public void onRegisterFailed(Name prefix) {
+ registered = false;
+ logger.log(Level.SEVERE, "Failed to register prefix: " + prefix.toUri());
+ }
+ }, new ForwardingFlags());
+ } catch (net.named_data.jndn.security.SecurityException e) {
+ registered = false;
+ throw new IOException("Failed to communicate to face due to security error", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addPipelineStage(PipelineStage<Data, Data> pipelineStage) {
+ pipeline.add(pipelineStage);
+ }
+
+ /**
+ * Process the {@link Data} before sending it; this runs the packet through
+ * each registered {@link PipelineStage} in order.
+ *
+ * @param data the {@link Data} to process
+ * @return a processed {@link Data} packet; no guarantee as to whether it is
+ * the same instance as passed in as a parameter (and likely not).
+ * @throws Exception if a pipeline stage fails
+ */
+ public Data processPipeline(Data data) throws Exception {
+ for (PipelineStage<Data, Data> stage : pipeline) {
+ data = stage.process(data);
+ }
+ return data;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run() {
+ if (!isRegistered()) {
+ try {
+ register();
+ } catch (IOException ex) {
+ throw new RuntimeException("Failed to register prefix, aborting.", ex);
+ }
+ }
+
+ // continuously serve packets
+ while (true) {
+ try {
+ synchronized (face) {
+ face.processEvents();
+ }
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "Failed to process events.", ex);
+ } catch (EncodingException ex) {
+ logger.log(Level.SEVERE, "Failed to parse bytes.", ex);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/pipeline/CompressionStage.java b/app/src/main/java/com/intel/jndn/utils/server/pipeline/CompressionStage.java
new file mode 100644
index 0000000..687bde0
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/pipeline/CompressionStage.java
@@ -0,0 +1,49 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server.pipeline;
+
+import com.intel.jndn.utils.server.PipelineStage;
+import java.io.ByteArrayOutputStream;
+import java.util.zip.GZIPOutputStream;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.util.Blob;
+
+/**
+ * Sample stage for compressing {@link Data} content using GZIP
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class CompressionStage implements PipelineStage<Data, Data> {
+
+ /**
+ * Compress and replace the {@link Data} content. Note: this stage will return
+ * the same {@link Data} instance and will modify only its content.
+ *
+ * @param context the {@link Data} packet
+ * @return the same packet but with GZIP-compressed content
+ * @throws Exception if compression fails
+ */
+ @Override
+ public Data process(Data context) throws Exception {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ try (GZIPOutputStream stream = new GZIPOutputStream(buffer)) {
+ stream.write(context.getContent().getImmutableArray(), 0, context.getContent().size());
+ stream.close();
+ }
+
+ context.setContent(new Blob(buffer.toByteArray()));
+ return context;
+ }
+
+}
diff --git a/app/src/main/java/com/intel/jndn/utils/server/pipeline/SigningStage.java b/app/src/main/java/com/intel/jndn/utils/server/pipeline/SigningStage.java
new file mode 100644
index 0000000..5296d89
--- /dev/null
+++ b/app/src/main/java/com/intel/jndn/utils/server/pipeline/SigningStage.java
@@ -0,0 +1,68 @@
+/*
+ * jndn-utils
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 3, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+package com.intel.jndn.utils.server.pipeline;
+
+import com.intel.jndn.utils.server.PipelineStage;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+
+/**
+ * As a part of a {@link com.intel.jndn.utils.ServerIn} pipeline, this stage
+ * will sign a {@link Data} packet.
+ *
+ * @author Andrew Brown <andrew.brown@intel.com>
+ */
+public class SigningStage implements PipelineStage<Data, Data> {
+
+ private final KeyChain keyChain;
+ private final Name certificateName;
+
+ /**
+ * Default constructor.
+ *
+ * @param keyChain the {@link KeyChain} to use for signing
+ * @param certificateName the certificate to sign with
+ */
+ public SigningStage(KeyChain keyChain, Name certificateName) {
+ this.keyChain = keyChain;
+ this.certificateName = certificateName;
+ }
+
+ /**
+ * Build the stage using the default certificate name defined on the
+ * {@link KeyChain}.
+ *
+ * @param keyChain the {@link KeyChain} to use for signing
+ * @throws SecurityException if no default certificate is found
+ */
+ public SigningStage(KeyChain keyChain) throws SecurityException {
+ this.keyChain = keyChain;
+ this.certificateName = keyChain.getDefaultCertificateName();
+ }
+
+ /**
+ * Sign a {@link Data} packet.
+ *
+ * @param context the data packet to sign
+ * @return the signed data packet
+ * @throws Exception if signing fails
+ */
+ @Override
+ public Data process(Data context) throws Exception {
+ keyChain.sign(context, certificateName);
+ return context;
+ }
+}