blob: b82051e4cabc46a4c445ef86eb33cb8418988982 [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);
andrewsbrownb005ee62015-03-31 14:45:54 -070074 Name name = hasSegment(interest.getName()) ? interest.getName().getPrefix(-1) : interest.getName();
75 return new SegmentedFutureData(name, segments);
andrewsbrown69d53292015-03-17 19:37:34 +010076 }
77
andrewsbrownb005ee62015-03-31 14:45:54 -070078 /**
andrewsbrown69d53292015-03-17 19:37:34 +010079 * Asynchronously send Interest packets for a segmented result; will block
80 * until the first packet is received and then send remaining interests until
81 * the specified FinalBlockId.
82 *
83 * @param face
andrewsbrownb005ee62015-03-31 14:45:54 -070084 * @param name the {@link Name} of the packet to retrieve using a default
85 * interest
andrewsbrown69d53292015-03-17 19:37:34 +010086 * @return an aggregated data packet from all received segments
87 */
88 public Future<Data> getAsync(Face face, Name name) {
89 return getAsync(face, SimpleClient.getDefaultInterest(name));
90 }
91
92 /**
93 * Asynchronously send Interest packets for a segmented result; will block
94 * until the first packet is received and then send remaining interests until
95 * the specified FinalBlockId.
96 *
97 * @param face
98 * @param interest should include either a ChildSelector or an initial segment
99 * number
100 * @return a list of FutureData packets; if the first segment fails, the list
101 * will contain one FutureData with the failure exception
102 */
103 public List<Future<Data>> getAsyncList(Face face, Interest interest) {
Andrew Browndb457052015-02-21 15:41:58 -0800104 // get first segment; default 0 or use a specified start segment
105 long firstSegment = 0;
106 boolean specifiedSegment = false;
107 try {
108 firstSegment = interest.getName().get(-1).toSegment();
109 specifiedSegment = true;
110 } catch (EncodingException e) {
111 // check for interest selector if no initial segment found
112 if (interest.getChildSelector() == -1) {
andrewsbrown0cf35f92015-03-09 12:00:00 -0700113 logger.log(Level.WARNING, "No child selector set for a segmented Interest; this may result in incorrect retrieval.");
andrewsbrown69d53292015-03-17 19:37:34 +0100114 // allow this interest to pass without a segment marker since it may still succeed
Andrew Browndb457052015-02-21 15:41:58 -0800115 }
116 }
117
118 // setup segments
andrewsbrown69d53292015-03-17 19:37:34 +0100119 final List<Future<Data>> segments = new ArrayList<>();
120 segments.add(SimpleClient.getDefault().getAsync(face, interest));
Andrew Browndb457052015-02-21 15:41:58 -0800121
andrewsbrown629816c2015-04-07 09:04:21 -0700122 // retrieve first packet and find the FinalBlockId
Andrew Browndb457052015-02-21 15:41:58 -0800123 long lastSegment;
124 try {
125 lastSegment = segments.get(0).get().getMetaInfo().getFinalBlockId().toSegment();
andrewsbrown629816c2015-04-07 09:04:21 -0700126 } catch (ExecutionException | InterruptedException e) {
andrewsbrown0cf35f92015-03-09 12:00:00 -0700127 logger.log(Level.SEVERE, "Failed to retrieve first segment: ", e);
andrewsbrown629816c2015-04-07 09:04:21 -0700128 ((FutureData) segments.get(0)).reject(e); // TODO implies knowledge of underlying data structure
129 return segments;
130 } catch (EncodingException e) {
131 logger.log(Level.FINER, "No FinalBlockId, returning first packet.");
andrewsbrown0cf35f92015-03-09 12:00:00 -0700132 return segments;
Andrew Browndb457052015-02-21 15:41:58 -0800133 }
134
135 // cut interest segment off
136 if (specifiedSegment) {
137 interest.setName(interest.getName().getPrefix(-1));
138 }
139
140 // send interests in remaining segments
141 for (long i = firstSegment + 1; i <= lastSegment; i++) {
142 Interest segmentedInterest = new Interest(interest);
143 segmentedInterest.getName().appendSegment(i);
andrewsbrown69d53292015-03-17 19:37:34 +0100144 Future<Data> futureData = SimpleClient.getDefault().getAsync(face, segmentedInterest);
Andrew Browndb457052015-02-21 15:41:58 -0800145 segments.add((int) i, futureData);
146 }
147
148 return segments;
149 }
150
151 /**
152 * Asynchronously send Interests for a segmented Data packet using a default
153 * interest (e.g. 2 second timeout); this will block until complete (i.e.
andrewsbrown0cf35f92015-03-09 12:00:00 -0700154 * either data is received or the interest times out). See getAsync(Face face)
155 * for more information.
Andrew Browndb457052015-02-21 15:41:58 -0800156 *
157 * @param face
158 * @param name
159 * @return
160 */
andrewsbrown69d53292015-03-17 19:37:34 +0100161 public List<Future<Data>> getAsyncList(Face face, Name name) {
162 return getAsyncList(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800163 }
164
165 /**
166 * Retrieve a segmented Data packet; will block until all segments are
167 * received and will re-assemble these.
168 *
169 * @param face
170 * @param interest should include either a ChildSelector or an initial segment
171 * number
andrewsbrown0cf35f92015-03-09 12:00:00 -0700172 * @return a Data packet; the name will inherit from the sent Interest, not
andrewsbrown69d53292015-03-17 19:37:34 +0100173 * the returned packets and the content will be a concatenation of all of the
174 * packet contents.
175 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800176 */
andrewsbrown69d53292015-03-17 19:37:34 +0100177 @Override
andrewsbrownb005ee62015-03-31 14:45:54 -0700178 public Data getSync(Face face, Interest interest) throws IOException {
andrewsbrown69d53292015-03-17 19:37:34 +0100179 try {
180 return getAsync(face, interest).get();
181 } catch (ExecutionException | InterruptedException e) {
182 logger.log(Level.WARNING, "Failed to retrieve data.", e);
183 throw new IOException("Failed to retrieve data.", e);
Andrew Browndb457052015-02-21 15:41:58 -0800184 }
Andrew Browndb457052015-02-21 15:41:58 -0800185 }
186
187 /**
188 * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
189 * second timeout); this will block until complete (i.e. either data is
andrewsbrown0cf35f92015-03-09 12:00:00 -0700190 * received or the interest times out). See getSync(Face face) for more
191 * information.
Andrew Browndb457052015-02-21 15:41:58 -0800192 *
193 * @param face
194 * @param name
195 * @return
andrewsbrown69d53292015-03-17 19:37:34 +0100196 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800197 */
andrewsbrown69d53292015-03-17 19:37:34 +0100198 public Data getSync(Face face, Name name) throws IOException {
199 return getSync(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800200 }
201
202 /**
203 * Check if a name ends in a segment component; uses marker value found in the
204 * NDN naming conventions (see
205 * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
206 *
207 * @param name
208 * @return
209 */
210 public static boolean hasSegment(Name name) {
211 return name.get(-1).getValue().buf().get(0) == 0x00;
212 }
Andrew Browndb457052015-02-21 15:41:58 -0800213}