blob: d9deb3e6881fd5200784234c2fb1667f6cde21cb [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;
19import java.util.ArrayList;
20import java.util.List;
21import java.util.concurrent.ExecutionException;
andrewsbrown69d53292015-03-17 19:37:34 +010022import java.util.concurrent.Future;
andrewsbrown0cf35f92015-03-09 12:00:00 -070023import java.util.logging.Level;
Andrew Browndb457052015-02-21 15:41:58 -080024import java.util.logging.Logger;
25import net.named_data.jndn.Data;
26import net.named_data.jndn.Face;
27import net.named_data.jndn.Interest;
28import net.named_data.jndn.Name;
29import net.named_data.jndn.encoding.EncodingException;
Andrew Browndb457052015-02-21 15:41:58 -080030
31/**
32 * Provide a client to simplify retrieving segmented Data packets over the NDN
33 * network. This class expects the Data producer to follow the NDN naming
34 * conventions (see http://named-data.net/doc/tech-memos/naming-conventions.pdf)
35 * and produce Data packets with a valid segment as the last component of their
36 * name; additionally, at least the first packet should set the FinalBlockId of
37 * the packet's MetaInfo (see
38 * http://named-data.net/doc/ndn-tlv/data.html#finalblockid).
39 *
40 * @author Andrew Brown <andrew.brown@intel.com>
41 */
andrewsbrown69d53292015-03-17 19:37:34 +010042public class SegmentedClient implements Client {
Andrew Browndb457052015-02-21 15:41:58 -080043
44 private static SegmentedClient defaultInstance;
45 private static final Logger logger = Logger.getLogger(SegmentedClient.class.getName());
Andrew Browndb457052015-02-21 15:41:58 -080046
47 /**
andrewsbrown69d53292015-03-17 19:37:34 +010048 * Singleton access for simpler client use.
Andrew Browndb457052015-02-21 15:41:58 -080049 *
50 * @return
51 */
52 public static SegmentedClient getDefault() {
53 if (defaultInstance == null) {
54 defaultInstance = new SegmentedClient();
55 }
56 return defaultInstance;
57 }
58
59 /**
60 * Asynchronously send Interest packets for a segmented result; will block
61 * until the first packet is received and then send remaining interests until
62 * the specified FinalBlockId.
63 *
64 * @param face
65 * @param interest should include either a ChildSelector or an initial segment
andrewsbrownb005ee62015-03-31 14:45:54 -070066 * number; the initial segment number will be cut off in the de-segmented
67 * packet.
andrewsbrown0cf35f92015-03-09 12:00:00 -070068 * @return a list of FutureData packets; if the first segment fails, the list
69 * will contain one FutureData with the failure exception
Andrew Browndb457052015-02-21 15:41:58 -080070 */
andrewsbrown69d53292015-03-17 19:37:34 +010071 @Override
72 public Future<Data> getAsync(Face face, Interest interest) {
73 List<Future<Data>> segments = getAsyncList(face, interest);
andrewsbrown7d68e082015-04-20 13:34:51 -070074 return new SegmentedFutureData(interest.getName(), segments);
andrewsbrown69d53292015-03-17 19:37:34 +010075 }
76
andrewsbrownb005ee62015-03-31 14:45:54 -070077 /**
andrewsbrown69d53292015-03-17 19:37:34 +010078 * Asynchronously send Interest packets for a segmented result; will block
79 * until the first packet is received and then send remaining interests until
80 * the specified FinalBlockId.
81 *
82 * @param face
andrewsbrownb005ee62015-03-31 14:45:54 -070083 * @param name the {@link Name} of the packet to retrieve using a default
84 * interest
andrewsbrown69d53292015-03-17 19:37:34 +010085 * @return an aggregated data packet from all received segments
86 */
87 public Future<Data> getAsync(Face face, Name name) {
88 return getAsync(face, SimpleClient.getDefaultInterest(name));
89 }
90
91 /**
92 * Asynchronously send Interest packets for a segmented result; will block
93 * until the first packet is received and then send remaining interests until
94 * the specified FinalBlockId.
95 *
96 * @param face
97 * @param interest should include either a ChildSelector or an initial segment
98 * number
99 * @return a list of FutureData packets; if the first segment fails, the list
100 * will contain one FutureData with the failure exception
101 */
102 public List<Future<Data>> getAsyncList(Face face, Interest interest) {
Andrew Browndb457052015-02-21 15:41:58 -0800103 // get first segment; default 0 or use a specified start segment
104 long firstSegment = 0;
Andrew Browndb457052015-02-21 15:41:58 -0800105 try {
106 firstSegment = interest.getName().get(-1).toSegment();
Andrew Browndb457052015-02-21 15:41:58 -0800107 } catch (EncodingException e) {
108 // check for interest selector if no initial segment found
109 if (interest.getChildSelector() == -1) {
andrewsbrown0cf35f92015-03-09 12:00:00 -0700110 logger.log(Level.WARNING, "No child selector set for a segmented Interest; this may result in incorrect retrieval.");
andrewsbrown69d53292015-03-17 19:37:34 +0100111 // allow this interest to pass without a segment marker since it may still succeed
Andrew Browndb457052015-02-21 15:41:58 -0800112 }
113 }
114
115 // setup segments
andrewsbrown69d53292015-03-17 19:37:34 +0100116 final List<Future<Data>> segments = new ArrayList<>();
117 segments.add(SimpleClient.getDefault().getAsync(face, interest));
Andrew Browndb457052015-02-21 15:41:58 -0800118
andrewsbrown629816c2015-04-07 09:04:21 -0700119 // retrieve first packet and find the FinalBlockId
andrewsbrown7d68e082015-04-20 13:34:51 -0700120 Data firstData;
Andrew Browndb457052015-02-21 15:41:58 -0800121 long lastSegment;
122 try {
andrewsbrown7d68e082015-04-20 13:34:51 -0700123 firstData = segments.get(0).get();
124 lastSegment = firstData.getMetaInfo().getFinalBlockId().toSegment();
andrewsbrown629816c2015-04-07 09:04:21 -0700125 } catch (ExecutionException | InterruptedException e) {
andrewsbrown2d7ee8d2015-04-15 12:40:58 -0700126 logger.log(Level.FINE, "Failed to retrieve first segment: " + interest.toUri(), e);
andrewsbrown629816c2015-04-07 09:04:21 -0700127 ((FutureData) segments.get(0)).reject(e); // TODO implies knowledge of underlying data structure
128 return segments;
129 } catch (EncodingException e) {
andrewsbrown2d7ee8d2015-04-15 12:40:58 -0700130 logger.log(Level.FINER, "No FinalBlockId, returning first packet: " + interest.toUri());
andrewsbrown0cf35f92015-03-09 12:00:00 -0700131 return segments;
Andrew Browndb457052015-02-21 15:41:58 -0800132 }
133
andrewsbrown7d68e082015-04-20 13:34:51 -0700134 // set follow-on segment names to match first segment retrieve
135 Interest interestCopy = new Interest(interest);
136 if (hasSegment(firstData.getName())) {
137 interestCopy.setName(firstData.getName().getPrefix(-1)); // cut last segment number if present
138 } else {
139 logger.log(Level.FINER, "First packet retrieved does not have a segment number, continuing on: " + firstData.getName().toUri());
140 interestCopy.setName(firstData.getName());
Andrew Browndb457052015-02-21 15:41:58 -0800141 }
142
143 // send interests in remaining segments
144 for (long i = firstSegment + 1; i <= lastSegment; i++) {
andrewsbrown7d68e082015-04-20 13:34:51 -0700145 Interest segmentedInterest = new Interest(interestCopy);
Andrew Browndb457052015-02-21 15:41:58 -0800146 segmentedInterest.getName().appendSegment(i);
andrewsbrown69d53292015-03-17 19:37:34 +0100147 Future<Data> futureData = SimpleClient.getDefault().getAsync(face, segmentedInterest);
Andrew Browndb457052015-02-21 15:41:58 -0800148 segments.add((int) i, futureData);
149 }
150
151 return segments;
152 }
153
154 /**
155 * Asynchronously send Interests for a segmented Data packet using a default
156 * interest (e.g. 2 second timeout); this will block until complete (i.e.
andrewsbrown0cf35f92015-03-09 12:00:00 -0700157 * either data is received or the interest times out). See getAsync(Face face)
158 * for more information.
Andrew Browndb457052015-02-21 15:41:58 -0800159 *
160 * @param face
161 * @param name
162 * @return
163 */
andrewsbrown69d53292015-03-17 19:37:34 +0100164 public List<Future<Data>> getAsyncList(Face face, Name name) {
165 return getAsyncList(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800166 }
167
168 /**
169 * Retrieve a segmented Data packet; will block until all segments are
170 * received and will re-assemble these.
171 *
172 * @param face
173 * @param interest should include either a ChildSelector or an initial segment
174 * number
andrewsbrown0cf35f92015-03-09 12:00:00 -0700175 * @return a Data packet; the name will inherit from the sent Interest, not
andrewsbrown69d53292015-03-17 19:37:34 +0100176 * the returned packets and the content will be a concatenation of all of the
177 * packet contents.
178 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800179 */
andrewsbrown69d53292015-03-17 19:37:34 +0100180 @Override
andrewsbrownb005ee62015-03-31 14:45:54 -0700181 public Data getSync(Face face, Interest interest) throws IOException {
andrewsbrown69d53292015-03-17 19:37:34 +0100182 try {
183 return getAsync(face, interest).get();
184 } catch (ExecutionException | InterruptedException e) {
andrewsbrown2d7ee8d2015-04-15 12:40:58 -0700185 String message = "Failed to retrieve data: " + interest.toUri();
186 logger.log(Level.FINE, message, e);
187 throw new IOException(message, e);
Andrew Browndb457052015-02-21 15:41:58 -0800188 }
Andrew Browndb457052015-02-21 15:41:58 -0800189 }
190
191 /**
192 * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
193 * second timeout); this will block until complete (i.e. either data is
andrewsbrown0cf35f92015-03-09 12:00:00 -0700194 * received or the interest times out). See getSync(Face face) for more
195 * information.
Andrew Browndb457052015-02-21 15:41:58 -0800196 *
197 * @param face
198 * @param name
199 * @return
andrewsbrown69d53292015-03-17 19:37:34 +0100200 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800201 */
andrewsbrown69d53292015-03-17 19:37:34 +0100202 public Data getSync(Face face, Name name) throws IOException {
203 return getSync(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800204 }
205
206 /**
207 * Check if a name ends in a segment component; uses marker value found in the
208 * NDN naming conventions (see
209 * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
210 *
211 * @param name
212 * @return
213 */
214 public static boolean hasSegment(Name name) {
215 return name.get(-1).getValue().buf().get(0) == 0x00;
216 }
Andrew Browndb457052015-02-21 15:41:58 -0800217}