blob: 9c9d61f5514fe2489ef1b10bd5210197da9e1f2f [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) {
86 firstData.resolve(data);
87 // now request subsequent segments using FinalBlockId and the Interest template
88 try {
89 long lastSegmentId = parseLastSegmentId(data);
90 Interest template = new Interest(interest);
91 template.setName(removeSegment(data.getName()));
92 requestRemainingSegments(face, segmentedData, template, firstSegmentId + 1, lastSegmentId);
93 } catch (EncodingException ex) {
94 Logger.getLogger(SegmentedClient.class.getName()).log(Level.SEVERE, null, ex);
95 }
96 }
97 }, new OnTimeout() {
98 @Override
99 public void onTimeout(Interest interest) {
100 segmentedData.reject(new TimeoutException());
101 }
102 });
103 } catch (IOException e) {
104 logger.log(Level.FINE, "IO failure while sending interest: ", e);
105 segmentedData.reject(e);
106 }
107
108 return segmentedData;
andrewsbrown69d53292015-03-17 19:37:34 +0100109 }
110
andrewsbrownb005ee62015-03-31 14:45:54 -0700111 /**
andrewsbrownd403db82015-05-11 13:02:28 -0700112 * @param interest the request {@link Interest}
113 * @return the first segment the interest is requesting, or 0 if none found
114 */
115 private long parseFirstSegmentId(Interest interest) {
116 try {
117 return interest.getName().get(-1).toSegment();
118 } catch (EncodingException e) {
119 if (interest.getChildSelector() == -1) {
120 logger.log(Level.WARNING, "No child selector set for a segmented Interest; this may result in incorrect retrieval.");
121 // allow this interest to pass without a segment marker since it may still succeed
122 }
123 return 0;
124 }
125 }
126
127 /**
128 * @param firstData the first returned {@link Data}
129 * @return the last segment number available as specified in the FinalBlockId
130 * @throws EncodingException
131 */
132 private long parseLastSegmentId(Data firstData) throws EncodingException {
133 return firstData.getMetaInfo().getFinalBlockId().toSegment();
134 }
135
136 /**
137 * Send interests for remaining segments; adding them to the segmented future
andrewsbrown69d53292015-03-17 19:37:34 +0100138 *
139 * @param face
andrewsbrownd403db82015-05-11 13:02:28 -0700140 * @param segmentedData
141 * @param template
142 * @param fromSegment
143 * @param toSegment
144 */
145 private void requestRemainingSegments(Face face, SegmentedFutureData segmentedData, Interest template, long fromSegment, long toSegment) {
146 // send interests in remaining segments
147 for (long i = fromSegment; i <= toSegment; i++) {
148 Interest segmentedInterest = new Interest(template);
149 segmentedInterest.getName().appendSegment(i);
150 Future<Data> futureData = SimpleClient.getDefault().getAsync(face, segmentedInterest);
151 segmentedData.add((int) i, futureData);
152 }
153 }
154
155 /**
156 * Asynchronously send Interest packets for a segmented result; see {@link #getAsync(net.named_data.jndn.Face, net.named_data.jndn.Interest)
157 * } for more information.
158 *
159 * @param face the {@link Face} on which to retrieve the packets
andrewsbrownb005ee62015-03-31 14:45:54 -0700160 * @param name the {@link Name} of the packet to retrieve using a default
andrewsbrownd403db82015-05-11 13:02:28 -0700161 * interest; may optionally end with the segment component of the first
162 * segment to retrieve
andrewsbrown69d53292015-03-17 19:37:34 +0100163 * @return an aggregated data packet from all received segments
164 */
165 public Future<Data> getAsync(Face face, Name name) {
166 return getAsync(face, SimpleClient.getDefaultInterest(name));
167 }
168
169 /**
andrewsbrownd403db82015-05-11 13:02:28 -0700170 * Retrieve a segmented Data packet; see {@link #getAsync(net.named_data.jndn.Face, net.named_data.jndn.Interest)
171 * } for more information. This method will block and call
172 * {@link Face#processEvents()} until the sent interests timeout or the
173 * corresponding data packets are retrieved.
Andrew Browndb457052015-02-21 15:41:58 -0800174 *
175 * @param face
176 * @param interest should include either a ChildSelector or an initial segment
177 * number
andrewsbrown0cf35f92015-03-09 12:00:00 -0700178 * @return a Data packet; the name will inherit from the sent Interest, not
andrewsbrown69d53292015-03-17 19:37:34 +0100179 * the returned packets and the content will be a concatenation of all of the
180 * packet contents.
181 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800182 */
andrewsbrown69d53292015-03-17 19:37:34 +0100183 @Override
andrewsbrownb005ee62015-03-31 14:45:54 -0700184 public Data getSync(Face face, Interest interest) throws IOException {
andrewsbrown69d53292015-03-17 19:37:34 +0100185 try {
186 return getAsync(face, interest).get();
187 } catch (ExecutionException | InterruptedException e) {
andrewsbrown2d7ee8d2015-04-15 12:40:58 -0700188 String message = "Failed to retrieve data: " + interest.toUri();
189 logger.log(Level.FINE, message, e);
190 throw new IOException(message, e);
Andrew Browndb457052015-02-21 15:41:58 -0800191 }
Andrew Browndb457052015-02-21 15:41:58 -0800192 }
193
194 /**
195 * Synchronously retrieve the Data for a Name using a default interest (e.g. 2
andrewsbrownd403db82015-05-11 13:02:28 -0700196 * second timeout). This method will block and call
197 * {@link Face#processEvents()} until the sent interests timeout or the
198 * corresponding data packets are retrieved.
Andrew Browndb457052015-02-21 15:41:58 -0800199 *
200 * @param face
201 * @param name
202 * @return
andrewsbrown69d53292015-03-17 19:37:34 +0100203 * @throws java.io.IOException
Andrew Browndb457052015-02-21 15:41:58 -0800204 */
andrewsbrown69d53292015-03-17 19:37:34 +0100205 public Data getSync(Face face, Name name) throws IOException {
206 return getSync(face, SimpleClient.getDefaultInterest(name));
Andrew Browndb457052015-02-21 15:41:58 -0800207 }
208
209 /**
210 * Check if a name ends in a segment component; uses marker value found in the
211 * NDN naming conventions (see
212 * http://named-data.net/doc/tech-memos/naming-conventions.pdf).
213 *
214 * @param name
215 * @return
216 */
217 public static boolean hasSegment(Name name) {
218 return name.get(-1).getValue().buf().get(0) == 0x00;
219 }
andrewsbrownd403db82015-05-11 13:02:28 -0700220
221 /**
222 * @param name the {@link Name} to check
223 * @return a new instance of {@link Name} with no segment component appended
224 */
225 public static Name removeSegment(Name name) {
226 return hasSegment(name) ? name.getPrefix(-1) : new Name(name);
227 }
Andrew Browndb457052015-02-21 15:41:58 -0800228}