blob: 023474000a30ca3a6bbde7d1edfd08f75ba913ce [file] [log] [blame]
Andrew Browndb457052015-02-21 15:41:58 -08001/*
andrewsbrown4feb2da2015-03-03 16:05:29 -08002 * jndn-utils
3 * Copyright (c) 2015, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 3, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
12 * more details.
Andrew Browndb457052015-02-21 15:41:58 -080013 */
14package com.intel.jndn.utils;
15
16import java.io.ByteArrayOutputStream;
17import java.io.IOException;
18import java.util.ArrayList;
19import java.util.List;
20import java.util.concurrent.ExecutionException;
21import java.util.logging.Logger;
22import net.named_data.jndn.Data;
23import net.named_data.jndn.Face;
24import net.named_data.jndn.Interest;
25import net.named_data.jndn.Name;
26import net.named_data.jndn.encoding.EncodingException;
27import net.named_data.jndn.util.Blob;
28
29/**
30 * Provide a client to simplify retrieving segmented Data packets over the NDN
31 * network. This class expects the Data producer to follow the NDN naming
32 * conventions (see http://named-data.net/doc/tech-memos/naming-conventions.pdf)
33 * and produce Data packets with a valid segment as the last component of their
34 * name; additionally, at least the first packet should set the FinalBlockId of
35 * the packet's MetaInfo (see
36 * http://named-data.net/doc/ndn-tlv/data.html#finalblockid).
37 *
38 * @author Andrew Brown <andrew.brown@intel.com>
39 */
40public class SegmentedClient {
41
42 private static SegmentedClient defaultInstance;
43 private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
44 private static final int SLEEP_TIME_MS = 10;
45
46 /**
47 * Singleton access for simpler client use
48 *
49 * @return
50 */
51 public static SegmentedClient getDefault() {
52 if (defaultInstance == null) {
53 defaultInstance = new SegmentedClient();
54 }
55 return defaultInstance;
56 }
57
58 /**
59 * Asynchronously send Interest packets for a segmented result; will block
60 * until the first packet is received and then send remaining interests until
61 * the specified FinalBlockId.
62 *
63 * @param face
64 * @param interest should include either a ChildSelector or an initial segment
65 * number
66 * @return
67 */
68 public List<FutureData> getAsync(Face face, Interest interest) {
69 // get first segment; default 0 or use a specified start segment
70 long firstSegment = 0;
71 boolean specifiedSegment = false;
72 try {
73 firstSegment = interest.getName().get(-1).toSegment();
74 specifiedSegment = true;
75 } catch (EncodingException e) {
76 // check for interest selector if no initial segment found
77 if (interest.getChildSelector() == -1) {
78 logger.warning("No child selector set for a segmented Interest; this may result in incorrect retrieval.");
79 }
80 }
81
82 // setup segments
83 final List<FutureData> segments = new ArrayList<>();
84 segments.add(Client.getDefault().getAsync(face, interest));
85
86 // retrieve first packet to find last segment value
87 long lastSegment;
88 try {
89 lastSegment = segments.get(0).get().getMetaInfo().getFinalBlockId().toSegment();
90 } catch (ExecutionException | InterruptedException | EncodingException e) {
91 logger.severe("Failed to retrieve first segment: " + e);
92 return null;
93 }
94
95 // cut interest segment off
96 if (specifiedSegment) {
97 interest.setName(interest.getName().getPrefix(-1));
98 }
99
100 // send interests in remaining segments
101 for (long i = firstSegment + 1; i <= lastSegment; i++) {
102 Interest segmentedInterest = new Interest(interest);
103 segmentedInterest.getName().appendSegment(i);
104 FutureData futureData = Client.getDefault().getAsync(face, segmentedInterest);
105 segments.add((int) i, futureData);
106 }
107
108 return segments;
109 }
110
111 /**
112 * Asynchronously send Interests for a segmented Data packet using a default
113 * interest (e.g. 2 second timeout); this will block until complete (i.e.
114 * either data is received or the interest times out).
115 *
116 * @param face
117 * @param name
118 * @return
119 */
120 public List<FutureData> getAsync(Face face, Name name) {
121 return getAsync(face, Client.getDefaultInterest(name));
122 }
123
124 /**
125 * Retrieve a segmented Data packet; will block until all segments are
126 * received and will re-assemble these.
127 *
128 * @param face
129 * @param interest should include either a ChildSelector or an initial segment
130 * number
131 * @return
132 */
133 public Data getSync(Face face, Interest interest) {
134 List<FutureData> segments = getAsync(face, interest);
135
136 // process events until complete
137 while (!isSegmentListComplete(segments)) {
138 try {
139 face.processEvents();
140 Thread.sleep(SLEEP_TIME_MS);
141 } catch (EncodingException | IOException e) {
142 logger.warning("Failed to retrieve data: " + e);
143 return null;
144 } catch (InterruptedException ex) {
145 // do nothing
146 }
147 }
148
149 // build final blob
150 ByteArrayOutputStream content = new ByteArrayOutputStream();
151 for (FutureData futureData : segments) {
152 try {
153 content.write(futureData.get().getContent().getImmutableArray());
154 } catch (ExecutionException | IOException | InterruptedException e) {
155 logger.warning("Failed to parse retrieved data: " + e);
156 return null;
157 }
158 }
159
160 Data data = new Data(interest.getName()); // TODO this name may not be correct; may need to contain additional suffixes
161 data.setContent(new Blob(content.toByteArray()));
162 return data;
163 }
164
165 /**
166 * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
167 * second timeout); this will block until complete (i.e. either data is
168 * received or the interest times out).
169 *
170 * @param face
171 * @param name
172 * @return
173 */
174 public Data getSync(Face face, Name name) {
175 return getSync(face, Client.getDefaultInterest(name));
176 }
177
178 /**
179 * Check if a name ends in a segment component; uses marker value found in the
180 * NDN naming conventions (see
181 * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
182 *
183 * @param name
184 * @return
185 */
186 public static boolean hasSegment(Name name) {
187 return name.get(-1).getValue().buf().get(0) == 0x00;
188 }
189
190 /**
191 * Check if a list of segments have returned from the network.
192 *
193 * @param segments
194 * @return
195 */
196 protected boolean isSegmentListComplete(List<FutureData> segments) {
197 for (FutureData futureData : segments) {
198 if (!futureData.isDone()) {
199 return false;
200 }
201 }
202 return true;
203 }
204}