blob: fbf9a2f4538a321e813c673f691703ea9eacebf8 [file] [log] [blame]
Andrew Brown3831baf2015-01-19 13:38:52 -08001/*
2 * File name: MockTransport.java
3 *
4 * Purpose: Use the MockTransport to mock sending data over the network.
5 *
6 * © Copyright Intel Corporation. All rights reserved.
7 * Intel Corporation, 2200 Mission College Boulevard,
8 * Santa Clara, CA 95052-8119, USA
9 */
10package com.intel.jndn.mock;
11
12import java.io.IOException;
13import java.util.HashMap;
14import java.util.Map.Entry;
15import net.named_data.jndn.Data;
16import net.named_data.jndn.ForwardingFlags;
17import net.named_data.jndn.Interest;
18import net.named_data.jndn.Name;
19import net.named_data.jndn.Node;
20import net.named_data.jndn.OnData;
21import net.named_data.jndn.OnInterest;
22import net.named_data.jndn.OnRegisterFailed;
23import net.named_data.jndn.OnTimeout;
24import net.named_data.jndn.encoding.EncodingException;
25import net.named_data.jndn.encoding.WireFormat;
26import org.apache.logging.log4j.LogManager;
27import org.apache.logging.log4j.Logger;
28
29/**
30 *
31 * @author Andrew Brown <andrew.brown@intel.com>
32 */
33public class MockFace {
34
35 private static final Logger logger = LogManager.getLogger();
36 private final Node node_;
37 HashMap<String, Data> responseMap = new HashMap<>();
38 HashMap<Long, MockOnInterestHandler> handlerMap = new HashMap<>();
39 long lastRegisteredId = 0;
40
41 /**
42 * Create a new Face to mock communication over the network; all packets are
43 * maintained in memory
44 */
45 public MockFace() {
46 node_ = new Node(new MockTransport(), null);
47 }
48
49 /**
50 * Add a response Data packet to send immediately when an Interest with a
51 * matching name is received; will continue to respond with the same packet
52 * over multiple requests. This will preempt any registered OnInterest
53 * handlers.
54 *
55 * @param name
56 * @param data
57 */
58 public void addResponse(Name name, Data data) {
59 logger.debug("Added response for: " + name.toUri());
60 responseMap.put(name.toUri(), data);
61 }
62
63 /**
64 * Stop sending a response for the given name.
65 *
66 * @param name
67 */
68 public void removeResponse(Name name) {
69 logger.debug("Removed response for: " + name.toUri());
70 responseMap.remove(name);
71 }
72
73 /**
74 * Handle incoming Interest packets; when an Interest is expressed through
75 * expressInterest(), this will run to determine if: 1) any responses have
76 * been registered or 2) if any OnInterest handlers have been registered.
77 * If one of these two succeeds, this method then re-directs the Interest from
78 * traveling down the network stack and returns data.
79 *
80 * @param interest
81 */
82 protected void handleIncomingRequests(Interest interest) {
83 String interestName = interest.getName().toUri();
84 long registeredPrefixId = findRegisteredHandler(interest);
85 // check if response registered
86 if (responseMap.containsKey(interestName)) {
87 logger.debug("Found response for: " + interestName);
88 Data data = responseMap.get(interestName);
89 ((MockTransport) node_.getTransport()).respondWith(data);
90 }
91 // check if handler registered
92 else if (registeredPrefixId != -1) {
93 logger.debug("Found handler for: " + interestName);
94 MockOnInterestHandler handler = handlerMap.get(findRegisteredHandler(interest));
95 handler.onInterest.onInterest(handler.prefix, interest, node_.getTransport(), registeredPrefixId);
96 }
97 // log failure
98 else {
99 logger.warn("No response found for interest (aborting): " + interestName);
100 }
101 }
102
103 /**
104 * Find a handler that matches the incoming interest; currently, the only
105 * flags supported are the ChildInherit flags.
106 *
107 * @param interest
108 * @return
109 */
110 protected long findRegisteredHandler(Interest interest) {
111 for (Entry<Long, MockOnInterestHandler> entry : handlerMap.entrySet()) {
112 MockOnInterestHandler handler = entry.getValue();
113 if (handler.flags.getChildInherit() && handler.prefix.match(interest.getName())) {
114 return entry.getKey();
115 }
116 if (handler.prefix.equals(interest.getName())) {
117 return entry.getKey();
118 }
119 }
120 return -1;
121 }
122
123 /**
124 * Helper class for holding references to OnInterest handlers
125 */
126 class MockOnInterestHandler {
127
128 Name prefix;
129 OnInterest onInterest;
130 ForwardingFlags flags;
131
132 public MockOnInterestHandler(Name prefix, OnInterest onInterest, ForwardingFlags flags) {
133 this.prefix = prefix;
134 this.onInterest = onInterest;
135 this.flags = flags;
136 }
137 }
138
139 /**
140 * Send the Interest through the transport, read the entire response and call
141 * onData(interest, data).
142 *
143 * @param interest The Interest to send. This copies the Interest.
144 * @param onData When a matching data packet is received, this calls
145 * onData.onData(interest, data) where interest is the interest given to
146 * expressInterest and data is the received Data object. NOTE: You must not
147 * change the interest object - if you need to change it then make a copy.
148 * @param onTimeout If the interest times out according to the interest
149 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
150 * interest given to expressInterest. If onTimeout is null, this does not use
151 * it.
152 * @param wireFormat A WireFormat object used to encode the message.
153 * @return The pending interest ID which can be used with
154 * removePendingInterest.
155 * @throws IOException For I/O error in sending the interest.
156 */
157 public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout,
158 WireFormat wireFormat) throws IOException {
159 long id = node_.expressInterest(interest, onData, onTimeout, wireFormat);
160 handleIncomingRequests(interest);
161 return id;
162 }
163
164 /**
165 * Send the Interest through the transport, read the entire response and call
166 * onData(interest, data). This uses the default
167 * WireFormat.getDefaultWireFormat().
168 *
169 * @param interest The Interest to send. This copies the Interest.
170 * @param onData When a matching data packet is received, this calls
171 * onData.onData(interest, data) where interest is the interest given to
172 * expressInterest and data is the received Data object. NOTE: You must not
173 * change the interest object - if you need to change it then make a copy.
174 * @param onTimeout If the interest times out according to the interest
175 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
176 * interest given to expressInterest. If onTimeout is null, this does not use
177 * it.
178 * @return The pending interest ID which can be used with
179 * removePendingInterest.
180 * @throws IOException For I/O error in sending the interest.
181 */
182 public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout) throws IOException {
183 return expressInterest(interest, onData, onTimeout, WireFormat.getDefaultWireFormat());
184 }
185
186 /**
187 * Send the Interest through the transport, read the entire response and call
188 * onData(interest, data). Ignore if the interest times out.
189 *
190 * @param interest The Interest to send. This copies the Interest.
191 * @param onData When a matching data packet is received, this calls
192 * onData.onData(interest, data) where interest is the interest given to
193 * expressInterest and data is the received Data object. NOTE: You must not
194 * change the interest object - if you need to change it then make a copy.
195 * @param wireFormat A WireFormat object used to encode the message.
196 * @return The pending interest ID which can be used with
197 * removePendingInterest.
198 * @throws IOException For I/O error in sending the interest.
199 */
200 public long expressInterest(Interest interest, OnData onData, WireFormat wireFormat) throws IOException {
201 return expressInterest(interest, onData, null, wireFormat);
202 }
203
204 /**
205 * Send the Interest through the transport, read the entire response and call
206 * onData(interest, data). Ignore if the interest times out. This uses the
207 * default WireFormat.getDefaultWireFormat().
208 *
209 * @param interest The Interest to send. This copies the Interest.
210 * @param onData When a matching data packet is received, this calls
211 * onData.onData(interest, data) where interest is the interest given to
212 * expressInterest and data is the received Data object. NOTE: You must not
213 * change the interest object - if you need to change it then make a copy.
214 * @return The pending interest ID which can be used with
215 * removePendingInterest.
216 * @throws IOException For I/O error in sending the interest.
217 */
218 public long expressInterest(Interest interest, OnData onData) throws IOException {
219 return expressInterest(interest, onData, null, WireFormat.getDefaultWireFormat());
220 }
221
222 /**
223 * Encode name as an Interest. If interestTemplate is not null, use its
224 * interest selectors. Send the interest through the transport, read the
225 * entire response and call onData(interest, data).
226 *
227 * @param name A Name for the interest. This copies the Name.
228 * @param interestTemplate If not null, copy interest selectors from the
229 * template. This does not keep a pointer to the Interest object.
230 * @param onData When a matching data packet is received, this calls
231 * onData.onData(interest, data) where interest is the interest given to
232 * expressInterest and data is the received Data object. NOTE: You must not
233 * change the interest object - if you need to change it then make a copy.
234 * @param onTimeout If the interest times out according to the interest
235 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
236 * interest given to expressInterest. If onTimeout is null, this does not use
237 * it.
238 * @param wireFormat A WireFormat object used to encode the message.
239 * @return The pending interest ID which can be used with
240 * removePendingInterest.
241 * @throws IOException For I/O error in sending the interest.
242 */
243 public long expressInterest(Name name, Interest interestTemplate, OnData onData, OnTimeout onTimeout,
244 WireFormat wireFormat) throws IOException {
245 Interest interest = new Interest(name);
246 if (interestTemplate != null) {
247 interest.setMinSuffixComponents(interestTemplate.getMinSuffixComponents());
248 interest.setMaxSuffixComponents(interestTemplate.getMaxSuffixComponents());
249 interest.setKeyLocator(interestTemplate.getKeyLocator());
250 interest.setExclude(interestTemplate.getExclude());
251 interest.setChildSelector(interestTemplate.getChildSelector());
252 interest.setMustBeFresh(interestTemplate.getMustBeFresh());
253 interest.setScope(interestTemplate.getScope());
254 interest.setInterestLifetimeMilliseconds(
255 interestTemplate.getInterestLifetimeMilliseconds());
256 // Don't copy the nonce.
257 } else {
258 interest.setInterestLifetimeMilliseconds(4000.0);
259 }
260
261 return expressInterest(interest, onData, onTimeout, wireFormat);
262 }
263
264 /**
265 * Encode name as an Interest. If interestTemplate is not null, use its
266 * interest selectors. Send the interest through the transport, read the
267 * entire response and call onData(interest, data). Use a default interest
268 * lifetime.
269 *
270 * @param name A Name for the interest. This copies the Name.
271 * @param onData When a matching data packet is received, this calls
272 * onData.onData(interest, data) where interest is the interest given to
273 * expressInterest and data is the received Data object. NOTE: You must not
274 * change the interest object - if you need to change it then make a copy.
275 * @param onTimeout If the interest times out according to the interest
276 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
277 * interest given to expressInterest. If onTimeout is null, this does not use
278 * it.
279 * @param wireFormat A WireFormat object used to encode the message.
280 * @return The pending interest ID which can be used with
281 * removePendingInterest.
282 * @throws IOException For I/O error in sending the interest.
283 */
284 public long expressInterest(Name name, OnData onData, OnTimeout onTimeout,
285 WireFormat wireFormat) throws IOException {
286 return expressInterest(name, null, onData, onTimeout, wireFormat);
287 }
288
289 /**
290 * Encode name as an Interest. If interestTemplate is not null, use its
291 * interest selectors. Send the interest through the transport, read the
292 * entire response and call onData(interest, data). Ignore if the interest
293 * times out.
294 *
295 * @param name A Name for the interest. This copies the Name.
296 * @param interestTemplate If not null, copy interest selectors from the
297 * template. This does not keep a pointer to the Interest object.
298 * @param onData When a matching data packet is received, this calls
299 * onData.onData(interest, data) where interest is the interest given to
300 * expressInterest and data is the received Data object. NOTE: You must not
301 * change the interest object - if you need to change it then make a copy.
302 * @param wireFormat A WireFormat object used to encode the message.
303 * @return The pending interest ID which can be used with
304 * removePendingInterest.
305 * @throws IOException For I/O error in sending the interest.
306 */
307 public long expressInterest(Name name, Interest interestTemplate, OnData onData,
308 WireFormat wireFormat) throws IOException {
309 return expressInterest(name, interestTemplate, onData, null, wireFormat);
310 }
311
312 /**
313 * Encode name as an Interest. If interestTemplate is not null, use its
314 * interest selectors. Send the interest through the transport, read the
315 * entire response and call onData(interest, data). This uses the default
316 * WireFormat.getDefaultWireFormat().
317 *
318 * @param name A Name for the interest. This copies the Name.
319 * @param interestTemplate If not null, copy interest selectors from the
320 * template. This does not keep a pointer to the Interest object.
321 * @param onData When a matching data packet is received, this calls
322 * onData.onData(interest, data) where interest is the interest given to
323 * expressInterest and data is the received Data object. NOTE: You must not
324 * change the interest object - if you need to change it then make a copy.
325 * @param onTimeout If the interest times out according to the interest
326 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
327 * interest given to expressInterest. If onTimeout is null, this does not use
328 * it.
329 * @return The pending interest ID which can be used with
330 * removePendingInterest.
331 * @throws IOException For I/O error in sending the interest.
332 */
333 public long expressInterest(Name name, Interest interestTemplate, OnData onData,
334 OnTimeout onTimeout) throws IOException {
335 return expressInterest(name, interestTemplate, onData, onTimeout,
336 WireFormat.getDefaultWireFormat());
337 }
338
339 /**
340 * Encode name as an Interest. If interestTemplate is not null, use its
341 * interest selectors. Send the interest through the transport, read the
342 * entire response and call onData(interest, data). Ignore if the interest
343 * times out. This uses the default WireFormat.getDefaultWireFormat().
344 *
345 * @param name A Name for the interest. This copies the Name.
346 * @param interestTemplate If not null, copy interest selectors from the
347 * template. This does not keep a pointer to the Interest object.
348 * @param onData When a matching data packet is received, this calls
349 * onData.onData(interest, data) where interest is the interest given to
350 * expressInterest and data is the received Data object. NOTE: You must not
351 * change the interest object - if you need to change it then make a copy.
352 * @return The pending interest ID which can be used with
353 * removePendingInterest.
354 * @throws IOException For I/O error in sending the interest.
355 */
356 public long expressInterest(Name name, Interest interestTemplate, OnData onData) throws IOException {
357 return expressInterest(name, interestTemplate, onData, null, WireFormat.getDefaultWireFormat());
358 }
359
360 /**
361 * Encode name as an Interest. If interestTemplate is not null, use its
362 * interest selectors. Send the interest through the transport, read the
363 * entire response and call onData(interest, data). Use a default interest
364 * lifetime. This uses the default WireFormat.getDefaultWireFormat().
365 *
366 * @param name A Name for the interest. This copies the Name.
367 * @param onData When a matching data packet is received, this calls
368 * onData.onData(interest, data) where interest is the interest given to
369 * expressInterest and data is the received Data object. NOTE: You must not
370 * change the interest object - if you need to change it then make a copy.
371 * @param onTimeout If the interest times out according to the interest
372 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
373 * interest given to expressInterest. If onTimeout is null, this does not use
374 * it.
375 * @return The pending interest ID which can be used with
376 * removePendingInterest.
377 * @throws IOException For I/O error in sending the interest.
378 */
379 public long expressInterest(Name name, OnData onData, OnTimeout onTimeout) throws IOException {
380 return expressInterest(name, null, onData, onTimeout, WireFormat.getDefaultWireFormat());
381 }
382
383 /**
384 * Encode name as an Interest. If interestTemplate is not null, use its
385 * interest selectors. Send the interest through the transport, read the
386 * entire response and call onData(interest, data). Use a default interest
387 * lifetime. Ignore if the interest times out.
388 *
389 * @param name A Name for the interest. This copies the Name.
390 * @param onData When a matching data packet is received, this calls
391 * onData.onData(interest, data) where interest is the interest given to
392 * expressInterest and data is the received Data object. NOTE: You must not
393 * change the interest object - if you need to change it then make a copy.
394 * @param wireFormat A WireFormat object used to encode the message.
395 * @return The pending interest ID which can be used with
396 * removePendingInterest.
397 * @throws IOException For I/O error in sending the interest.
398 */
399 public long expressInterest(Name name, OnData onData, WireFormat wireFormat) throws IOException {
400 return expressInterest(name, null, onData, null, wireFormat);
401 }
402
403 /**
404 * Encode name as an Interest. If interestTemplate is not null, use its
405 * interest selectors. Send the interest through the transport, read the
406 * entire response and call onData(interest, data). Use a default interest
407 * lifetime. Ignore if the interest times out. This uses the default
408 * WireFormat.getDefaultWireFormat().
409 *
410 * @param name A Name for the interest. This copies the Name.
411 * @param onData When a matching data packet is received, this calls
412 * onData.onData(interest, data) where interest is the interest given to
413 * expressInterest and data is the received Data object. NOTE: You must not
414 * change the interest object - if you need to change it then make a copy.
415 * @return The pending interest ID which can be used with
416 * removePendingInterest.
417 * @throws IOException For I/O error in sending the interest.
418 */
419 public long expressInterest(Name name, OnData onData) throws IOException {
420 return expressInterest(name, null, onData, null, WireFormat.getDefaultWireFormat());
421 }
422
423 /**
424 * Remove the pending interest entry with the pendingInterestId from the
425 * pending interest table. This does not affect another pending interest with
426 * a different pendingInterestId, even if it has the same interest name. If
427 * there is no entry with the pendingInterestId, do nothing.
428 *
429 * @param pendingInterestId The ID returned from expressInterest.
430 */
431 public void removePendingInterest(long pendingInterestId) {
432 node_.removePendingInterest(pendingInterestId);
433 }
434
435 /**
436 * Register prefix with the connected NDN hub and call onInterest when a
437 * matching interest is received. If you have not called
438 * setCommandSigningInfo, this assumes you are connecting to NDNx. If you have
439 * called setCommandSigningInfo, this first sends an NFD registration request,
440 * and if that times out then this sends an NDNx registration request. If you
441 * need to register a prefix with NFD, you must first call
442 * setCommandSigningInfo.
443 *
444 * @param prefix A Name for the prefix to register. This copies the Name.
445 * @param onInterest When an interest is received which matches the name
446 * prefix, this calls onInterest.onInterest(prefix, interest, transport,
447 * registeredPrefixId). NOTE: You must not change the prefix object - if you
448 * need to change it then make a copy.
449 * @param onRegisterFailed If register prefix fails for any reason, this calls
450 * onRegisterFailed.onRegisterFailed(prefix).
451 * @param flags The flags for finer control of which interests are forwarded
452 * to the application.
453 * @param wireFormat A WireFormat object used to encode the message.
454 * @return The lastRegisteredId prefix ID which can be used with
455 * removeRegisteredPrefix.
456 * @throws IOException For I/O error in sending the registration request.
457 * @throws SecurityException If signing a command interest for NFD and cannot
458 * find the private key for the certificateName.
459 */
460 public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
461 ForwardingFlags flags, WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
462 lastRegisteredId++;
463 handlerMap.put(lastRegisteredId, new MockOnInterestHandler(prefix, onInterest, flags));
464 return lastRegisteredId;
465 }
466
467 /**
468 * Register prefix with the connected NDN hub and call onInterest when a
469 * matching interest is received. This uses the default
470 * WireFormat.getDefaultWireFormat().
471 *
472 * @param prefix A Name for the prefix to register. This copies the Name.
473 * @param onInterest When an interest is received which matches the name
474 * prefix, this calls onInterest.onInterest(prefix, interest, transport,
475 * registeredPrefixId). NOTE: You must not change the prefix object - if you
476 * need to change it then make a copy.
477 * @param onRegisterFailed If register prefix fails for any reason, this calls
478 * onRegisterFailed.onRegisterFailed(prefix).
479 * @param flags The flags for finer control of which interests are forwarded
480 * to the application.
481 * @return The lastRegisteredId prefix ID which can be used with
482 * removeRegisteredPrefix.
483 * @throws IOException For I/O error in sending the registration request.
484 */
485 public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
486 ForwardingFlags flags) throws IOException, net.named_data.jndn.security.SecurityException {
487 return registerPrefix(prefix, onInterest, onRegisterFailed, flags,
488 WireFormat.getDefaultWireFormat());
489 }
490
491 /**
492 * Register prefix with the connected NDN hub and call onInterest when a
493 * matching interest is received. Use default ForwardingFlags.
494 *
495 * @param prefix A Name for the prefix to register. This copies the Name.
496 * @param onInterest When an interest is received which matches the name
497 * prefix, this calls onInterest.onInterest(prefix, interest, transport,
498 * registeredPrefixId). NOTE: You must not change the prefix object - if you
499 * need to change it then make a copy.
500 * @param onRegisterFailed If register prefix fails for any reason, this calls
501 * onRegisterFailed.onRegisterFailed(prefix).
502 * @param wireFormat A WireFormat object used to encode the message.
503 * @return The lastRegisteredId prefix ID which can be used with
504 * removeRegisteredPrefix.
505 * @throws IOException For I/O error in sending the registration request.
506 * @throws SecurityException If signing a command interest for NFD and cannot
507 * find the private key for the certificateName.
508 */
509 public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
510 WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
511 return registerPrefix(prefix, onInterest, onRegisterFailed, new ForwardingFlags(), wireFormat);
512 }
513
514 /**
515 * Register prefix with the connected NDN hub and call onInterest when a
516 * matching interest is received. This uses the default
517 * WireFormat.getDefaultWireFormat(). Use default ForwardingFlags.
518 *
519 * @param prefix A Name for the prefix to register. This copies the Name.
520 * @param onInterest When an interest is received which matches the name
521 * prefix, this calls onInterest.onInterest(prefix, interest, transport,
522 * registeredPrefixId). NOTE: You must not change the prefix object - if you
523 * need to change it then make a copy.
524 * @param onRegisterFailed If register prefix fails for any reason, this calls
525 * onRegisterFailed.onRegisterFailed(prefix).
526 * @return The lastRegisteredId prefix ID which can be used with
527 * removeRegisteredPrefix.
528 * @throws IOException For I/O error in sending the registration request.
529 * @throws SecurityException If signing a command interest for NFD and cannot
530 * find the private key for the certificateName.
531 */
532 public long registerPrefix(Name prefix, OnInterest onInterest,
533 OnRegisterFailed onRegisterFailed) throws IOException, net.named_data.jndn.security.SecurityException {
534 return registerPrefix(prefix, onInterest, onRegisterFailed, new ForwardingFlags(),
535 WireFormat.getDefaultWireFormat());
536 }
537
538 /**
539 * Remove the lastRegisteredId prefix entry with the registeredPrefixId from
540 * the lastRegisteredId prefix table. This does not affect another
541 * lastRegisteredId prefix with a different registeredPrefixId, even if it has
542 * the same prefix name. If there is no entry with the registeredPrefixId, do
543 * nothing.
544 *
545 * @param registeredPrefixId The ID returned from registerPrefix.
546 */
547 public void removeRegisteredPrefix(long registeredPrefixId) {
548 handlerMap.remove(registeredPrefixId);
549 }
550
551 /**
552 * Process any packets to receive and call callbacks such as onData,
553 * onInterest or onTimeout. This returns immediately if there is no data to
554 * receive. This blocks while calling the callbacks. You should repeatedly
555 * call this from an event loop, with calls to sleep as needed so that the
556 * loop doesn’t use 100% of the CPU. Since processEvents modifies the pending
557 * interest table, your application should make sure that it calls
558 * processEvents in the same thread as expressInterest (which also modifies
559 * the pending interest table). This may throw an exception for reading data
560 * or in the callback for processing the data. If you call this from an main
561 * event loop, you may want to catch and log/disregard all exceptions.
562 */
563 public void processEvents() throws IOException, EncodingException {
564 // Just call Node's processEvents.
565 node_.processEvents();
566 }
567
568 /**
569 * Shut down and disconnect this Face.
570 */
571 public void shutdown() {
572 node_.shutdown();
573 }
574}