blob: 28ac66b5888c8a0ecff84cf8195a8484094df22c [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
andrewsbrown629816c2015-04-07 09:04:21 -070016import com.intel.jndn.utils.client.FutureData;
andrewsbrown69d53292015-03-17 19:37:34 +010017import com.intel.jndn.utils.client.SegmentedFutureData;
Andrew Browndb457052015-02-21 15:41:58 -080018import java.io.IOException;
Andrew Browndb457052015-02-21 15:41:58 -080019import java.util.concurrent.ExecutionException;
andrewsbrown69d53292015-03-17 19:37:34 +010020import java.util.concurrent.Future;
andrewsbrownd403db82015-05-11 13:02:28 -070021import java.util.concurrent.TimeoutException;
andrewsbrown0cf35f92015-03-09 12:00:00 -070022import java.util.logging.Level;
Andrew Browndb457052015-02-21 15:41:58 -080023import java.util.logging.Logger;
24import net.named_data.jndn.Data;
25import net.named_data.jndn.Face;
26import net.named_data.jndn.Interest;
27import net.named_data.jndn.Name;
andrewsbrownd403db82015-05-11 13:02:28 -070028import net.named_data.jndn.OnData;
29import net.named_data.jndn.OnTimeout;
Andrew Browndb457052015-02-21 15:41:58 -080030import net.named_data.jndn.encoding.EncodingException;
Andrew Browndb457052015-02-21 15:41:58 -080031
32/**
33 * Provide a client to simplify retrieving segmented Data packets over the NDN
34 * network. This class expects the Data producer to follow the NDN naming
35 * conventions (see http://named-data.net/doc/tech-memos/naming-conventions.pdf)
36 * and produce Data packets with a valid segment as the last component of their
37 * name; additionally, at least the first packet should set the FinalBlockId of
38 * the packet's MetaInfo (see
39 * http://named-data.net/doc/ndn-tlv/data.html#finalblockid).
40 *
41 * @author Andrew Brown <andrew.brown@intel.com>
42 */
andrewsbrown69d53292015-03-17 19:37:34 +010043public class SegmentedClient implements Client {
Andrew Browndb457052015-02-21 15:41:58 -080044
45 private static SegmentedClient defaultInstance;
46 private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
Andrew Browndb457052015-02-21 15:41:58 -080047
48 /**
andrewsbrown69d53292015-03-17 19:37:34 +010049 * Singleton access for simpler client use.
Andrew Browndb457052015-02-21 15:41:58 -080050 *
51 * @return
52 */
53 public static SegmentedClient getDefault() {
54 if (defaultInstance == null) {
55 defaultInstance = new SegmentedClient();
56 }
57 return defaultInstance;
58 }
59
60 /**
andrewsbrownd403db82015-05-11 13:02:28 -070061 * Asynchronously send Interest packets for a segmented result; will not
62 * block, but will wait for the first packet to return before sending
63 * remaining interests until using the specified FinalBlockId. Will retrieve
64 * non-segmented packets as well.
Andrew Browndb457052015-02-21 15:41:58 -080065 *
andrewsbrownd403db82015-05-11 13:02:28 -070066 * @param face the {@link Face} on which to retrieve the packets
Andrew Browndb457052015-02-21 15:41:58 -080067 * @param interest should include either a ChildSelector or an initial segment
andrewsbrownb005ee62015-03-31 14:45:54 -070068 * number; the initial segment number will be cut off in the de-segmented
69 * packet.
andrewsbrown0cf35f92015-03-09 12:00:00 -070070 * @return a list of FutureData packets; if the first segment fails, the list
71 * will contain one FutureData with the failure exception
Andrew Browndb457052015-02-21 15:41:58 -080072 */
andrewsbrown69d53292015-03-17 19:37:34 +010073 @Override
andrewsbrownd403db82015-05-11 13:02:28 -070074 public Future<Data> getAsync(final Face face, Interest interest) {
75 final long firstSegmentId = parseFirstSegmentId(interest);
76 final SegmentedFutureData segmentedData = new SegmentedFutureData(face, interest.getName());
77 final FutureData firstData = new FutureData(face, interest.getName());
78 segmentedData.add(0, firstData);
79
80 // send first interest
81 logger.log(Level.FINER, "Sending first interest for: " + interest.getName().toUri());
82 try {
83 face.expressInterest(interest, new OnData() {
84 @Override
85 public void onData(Interest interest, Data data) {
andrewsbrown73065d02015-05-11 13:09:56 -070086 // request subsequent segments using FinalBlockId and the Interest template
andrewsbrownd403db82015-05-11 13:02:28 -070087 try {
88 long lastSegmentId = parseLastSegmentId(data);
89 Interest template = new Interest(interest);
90 template.setName(removeSegment(data.getName()));
91 requestRemainingSegments(face, segmentedData, template, firstSegmentId + 1, lastSegmentId);
92 } catch (EncodingException ex) {
andrewsbrown73065d02015-05-11 13:09:56 -070093 logger.log(Level.FINER, "No segment ID found in FinalBlockId, assuming first packet is only packet.");
andrewsbrownd403db82015-05-11 13:02:28 -070094 }
andrewsbrown73065d02015-05-11 13:09:56 -070095
96 // resolve the first data
97 firstData.resolve(data);
andrewsbrownd403db82015-05-11 13:02:28 -070098 }
99 }, new OnTimeout() {
100 @Override
101 public void onTimeout(Interest interest) {
102 segmentedData.reject(new TimeoutException());
103 }
104 });
105 } catch (IOException e) {
106 logger.log(Level.FINE, "IO failure while sending interest: ", e);
107 segmentedData.reject(e);
108 }
109
110 return segmentedData;
andrewsbrown69d53292015-03-17 19:37:34 +0100111 }
112
andrewsbrownb005ee62015-03-31 14:45:54 -0700113 /**
andrewsbrownd403db82015-05-11 13:02:28 -0700114 * @param interest the request {@link Interest}
115 * @return the first segment the interest is requesting, or 0 if none found
116 */
117 private long parseFirstSegmentId(Interest interest) {
118 try {
119 return interest.getName().get(-1).toSegment();
120 } catch (EncodingException e) {
121 if (interest.getChildSelector() == -1) {
122 logger.log(Level.WARNING, "No child selector set for a segmented Interest; this may result in incorrect retrieval.");
123 // allow this interest to pass without a segment marker since it may still succeed
124 }
125 return 0;
126 }
127 }
128
129 /**
130 * @param firstData the first returned {@link Data}
131 * @return the last segment number available as specified in the FinalBlockId
132 * @throws EncodingException
133 */
134 private long parseLastSegmentId(Data firstData) throws EncodingException {
135 return firstData.getMetaInfo().getFinalBlockId().toSegment();
136 }
137
138 /**
139 * Send interests for remaining segments; adding them to the segmented future
andrewsbrown69d53292015-03-17 19:37:34 +0100140 *
141 * @param face
andrewsbrownd403db82015-05-11 13:02:28 -0700142 * @param segmentedData
143 * @param template
144 * @param fromSegment
145 * @param toSegment
146 */
147 private void requestRemainingSegments(Face face, SegmentedFutureData segmentedData, Interest template, long fromSegment, long toSegment) {
148 // send interests in remaining segments
149 for (long i = fromSegment; i <= toSegment; i++) {
150 Interest segmentedInterest = new Interest(template);
151 segmentedInterest.getName().appendSegment(i);
152 Future<Data> futureData = SimpleClient.getDefault().getAsync(face, segmentedInterest);
153 segmentedData.add((int) i, futureData);
154 }
155 }
156
157 /**
158 * Asynchronously send Interest packets for a segmented result; see {@link #getAsync(net.named_data.jndn.Face, net.named_data.jndn.Interest)
159 * } for more information.
160 *
161 * @param face the {@link Face} on which to retrieve the packets
andrewsbrownb005ee62015-03-31 14:45:54 -0700162 * @param name the {@link Name} of the packet to retrieve using a default
andrewsbrownd403db82015-05-11 13:02:28 -0700163 * interest; may optionally end with the segment component of the first
164 * segment to retrieve
andrewsbrown69d53292015-03-17 19:37:34 +0100165 * @return an aggregated data packet from all received segments
166 */
167 public Future<Data> getAsync(Face face, Name name) {
168 return getAsync(face, SimpleClient.getDefaultInterest(name));
169 }
170
171 /**
andrewsbrownd403db82015-05-11 13:02:28 -0700172 * Retrieve a segmented Data packet; see {@link #getAsync(net.named_data.jndn.Face, net.named_data.jndn.Interest)
173 * } for more information. This method will block and call
174 * {@link Face#processEvents()} until the sent interests timeout or the
175 * corresponding data packets are retrieved.
Andrew Browndb457052015-02-21 15:41:58 -0800176 *
177 * @param face
178 * @param interest should include either a ChildSelector or an initial segment
179 * number
andrewsbrown0cf35f92015-03-09 12:00:00 -0700180 * @return a Data packet; the name will inherit from the sent Interest, not
andrewsbrown69d53292015-03-17 19:37:34 +0100181 * the returned packets and the content will be a concatenation of all of the
182 * packet contents.
183 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800184 */
andrewsbrown69d53292015-03-17 19:37:34 +0100185 @Override
andrewsbrownb005ee62015-03-31 14:45:54 -0700186 public Data getSync(Face face, Interest interest) throws IOException {
andrewsbrown69d53292015-03-17 19:37:34 +0100187 try {
188 return getAsync(face, interest).get();
189 } catch (ExecutionException | InterruptedException e) {
andrewsbrown2d7ee8d2015-04-15 12:40:58 -0700190 String message = "Failed to retrieve data: " + interest.toUri();
191 logger.log(Level.FINE, message, e);
192 throw new IOException(message, e);
Andrew Browndb457052015-02-21 15:41:58 -0800193 }
Andrew Browndb457052015-02-21 15:41:58 -0800194 }
195
196 /**
197 * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
andrewsbrownd403db82015-05-11 13:02:28 -0700198 * second timeout). This method will block and call
199 * {@link Face#processEvents()} until the sent interests timeout or the
200 * corresponding data packets are retrieved.
Andrew Browndb457052015-02-21 15:41:58 -0800201 *
202 * @param face
203 * @param name
204 * @return
andrewsbrown69d53292015-03-17 19:37:34 +0100205 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800206 */
andrewsbrown69d53292015-03-17 19:37:34 +0100207 public Data getSync(Face face, Name name) throws IOException {
208 return getSync(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800209 }
210
211 /**
212 * Check if a name ends in a segment component; uses marker value found in the
213 * NDN naming conventions (see
214 * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
215 *
216 * @param name
217 * @return
218 */
219 public static boolean hasSegment(Name name) {
220 return name.get(-1).getValue().buf().get(0) == 0x00;
221 }
andrewsbrownd403db82015-05-11 13:02:28 -0700222
223 /**
224 * @param name the {@link Name} to check
225 * @return a new instance of {@link Name} with no segment component appended
226 */
227 public static Name removeSegment(Name name) {
228 return hasSegment(name) ? name.getPrefix(-1) : new Name(name);
229 }
Andrew Browndb457052015-02-21 15:41:58 -0800230}