blob: d858d25add3e660d687b8bbe1270606951377b7c [file] [log] [blame]
Andrew Brown3831baf2015-01-19 13:38:52 -08001/*
andrewsbrown533c6ef2015-03-03 16:08:41 -08002 * jndn-mock
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 Brown3831baf2015-01-19 13:38:52 -080013 */
14package com.intel.jndn.mock;
15
16import java.io.IOException;
andrewsbrown1f28bcf2015-04-20 13:29:20 -070017import java.nio.ByteBuffer;
Andrew Brown3831baf2015-01-19 13:38:52 -080018import java.util.HashMap;
19import java.util.Map.Entry;
Andrew Brownec1b0d02015-02-21 13:11:42 -080020import java.util.logging.Logger;
Andrew Brown3831baf2015-01-19 13:38:52 -080021import net.named_data.jndn.Data;
Andrew Brownb91e6902015-02-12 09:01:50 -080022import net.named_data.jndn.Face;
Andrew Brown3831baf2015-01-19 13:38:52 -080023import net.named_data.jndn.ForwardingFlags;
24import net.named_data.jndn.Interest;
andrewsbrown1f28bcf2015-04-20 13:29:20 -070025import net.named_data.jndn.InterestFilter;
Andrew Brown3831baf2015-01-19 13:38:52 -080026import net.named_data.jndn.Name;
27import net.named_data.jndn.Node;
28import net.named_data.jndn.OnData;
29import net.named_data.jndn.OnInterest;
andrewsbrown1f28bcf2015-04-20 13:29:20 -070030import net.named_data.jndn.OnInterestCallback;
Andrew Brown3831baf2015-01-19 13:38:52 -080031import net.named_data.jndn.OnRegisterFailed;
32import net.named_data.jndn.OnTimeout;
33import net.named_data.jndn.encoding.EncodingException;
34import net.named_data.jndn.encoding.WireFormat;
andrewsbrown1f28bcf2015-04-20 13:29:20 -070035import net.named_data.jndn.security.SecurityException;
andrewsbrownebb72a82015-03-31 13:31:18 -070036import net.named_data.jndn.transport.Transport;
Andrew Brown3831baf2015-01-19 13:38:52 -080037
38/**
Andrew Brownb91e6902015-02-12 09:01:50 -080039 * <p>
40 * Use the MockTransport to mock sending data over the network. Allows for
41 * testing NDN applications while simulating network IO. TODO implement longest
42 * prefix match here for comprehensive testing.
43 * </p>
44 * <p>
45 * Usage
46 * </p>
47 * <pre><code>
48 * Face mockFace = new MockFace();
49 * mockFace.registerPrefix(...); // as usual
50 * mockFace.expressInterest(...); // as usual
51 *
52 * // also, simply inject a response that will be returned for an expressed interest
53 * mockFace.addResponse(interestName, data);
54 * </pre></code>
Andrew Brown3831baf2015-01-19 13:38:52 -080055 *
56 * @author Andrew Brown <andrew.brown@intel.com>
57 */
andrewsbrown1f28bcf2015-04-20 13:29:20 -070058public class MockFace extends FaceExtension {
Andrew Brown3831baf2015-01-19 13:38:52 -080059
Andrew Brownec1b0d02015-02-21 13:11:42 -080060 private static final Logger logger = Logger.getLogger(MockFace.class.getName());
Andrew Brown3831baf2015-01-19 13:38:52 -080061 private final Node node_;
62 HashMap<String, Data> responseMap = new HashMap<>();
63 HashMap<Long, MockOnInterestHandler> handlerMap = new HashMap<>();
andrewsbrown2304a342015-09-16 20:09:53 +010064 long lastPendingInterestId = 0;
65 long lastInterestFilterId = 0;
66 long lastRegisteredPrefixId = 0;
Andrew Brown3831baf2015-01-19 13:38:52 -080067
68 /**
69 * Create a new Face to mock communication over the network; all packets are
70 * maintained in memory
71 */
72 public MockFace() {
73 node_ = new Node(new MockTransport(), null);
74 }
75
76 /**
andrewsbrownebb72a82015-03-31 13:31:18 -070077 * @return a reference to the current MockTransport
78 */
79 public MockTransport getTransport() {
80 return (MockTransport) node_.getTransport();
81 }
82
83 /**
Andrew Brown3831baf2015-01-19 13:38:52 -080084 * Add a response Data packet to send immediately when an Interest with a
85 * matching name is received; will continue to respond with the same packet
86 * over multiple requests. This will preempt any registered OnInterest
87 * handlers.
88 *
89 * @param name
90 * @param data
91 */
92 public void addResponse(Name name, Data data) {
Andrew Brownec1b0d02015-02-21 13:11:42 -080093 logger.fine("Added response for: " + name.toUri());
Andrew Brown3831baf2015-01-19 13:38:52 -080094 responseMap.put(name.toUri(), data);
95 }
96
97 /**
98 * Stop sending a response for the given name.
99 *
100 * @param name
101 */
102 public void removeResponse(Name name) {
Andrew Brownec1b0d02015-02-21 13:11:42 -0800103 logger.fine("Removed response for: " + name.toUri());
Andrew Brown3831baf2015-01-19 13:38:52 -0800104 responseMap.remove(name);
105 }
106
107 /**
108 * Handle incoming Interest packets; when an Interest is expressed through
109 * expressInterest(), this will run to determine if: 1) any responses have
Andrew Brown8d8535b2015-01-19 15:22:06 -0800110 * been registered or 2) if any OnInterest handlers have been registered. If
111 * one of these two succeeds, this method then re-directs the Interest from
Andrew Brown3831baf2015-01-19 13:38:52 -0800112 * traveling down the network stack and returns data.
Andrew Brown8d8535b2015-01-19 15:22:06 -0800113 *
Andrew Brown3831baf2015-01-19 13:38:52 -0800114 * @param interest
115 */
116 protected void handleIncomingRequests(Interest interest) {
117 String interestName = interest.getName().toUri();
118 long registeredPrefixId = findRegisteredHandler(interest);
119 // check if response registered
120 if (responseMap.containsKey(interestName)) {
Andrew Brownec1b0d02015-02-21 13:11:42 -0800121 logger.fine("Found response for: " + interestName);
Andrew Brown3831baf2015-01-19 13:38:52 -0800122 Data data = responseMap.get(interestName);
123 ((MockTransport) node_.getTransport()).respondWith(data);
Andrew Brown8d8535b2015-01-19 15:22:06 -0800124 } // check if handler registered
Andrew Brown3831baf2015-01-19 13:38:52 -0800125 else if (registeredPrefixId != -1) {
Andrew Brownec1b0d02015-02-21 13:11:42 -0800126 logger.fine("Found handler for: " + interestName);
Andrew Brown3831baf2015-01-19 13:38:52 -0800127 MockOnInterestHandler handler = handlerMap.get(findRegisteredHandler(interest));
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700128 handler.signal(interest, registeredPrefixId);
Andrew Brown8d8535b2015-01-19 15:22:06 -0800129 } // log failure
Andrew Brown3831baf2015-01-19 13:38:52 -0800130 else {
Andrew Brownec1b0d02015-02-21 13:11:42 -0800131 logger.warning("No response found for interest (aborting): " + interestName);
Andrew Brown3831baf2015-01-19 13:38:52 -0800132 }
133 }
134
135 /**
136 * Find a handler that matches the incoming interest; currently, the only
137 * flags supported are the ChildInherit flags.
Andrew Brown8d8535b2015-01-19 15:22:06 -0800138 *
Andrew Brown3831baf2015-01-19 13:38:52 -0800139 * @param interest
Andrew Brown8d8535b2015-01-19 15:22:06 -0800140 * @return
Andrew Brown3831baf2015-01-19 13:38:52 -0800141 */
142 protected long findRegisteredHandler(Interest interest) {
143 for (Entry<Long, MockOnInterestHandler> entry : handlerMap.entrySet()) {
144 MockOnInterestHandler handler = entry.getValue();
145 if (handler.flags.getChildInherit() && handler.prefix.match(interest.getName())) {
146 return entry.getKey();
147 }
148 if (handler.prefix.equals(interest.getName())) {
149 return entry.getKey();
150 }
151 }
152 return -1;
153 }
154
155 /**
156 * Helper class for holding references to OnInterest handlers
157 */
158 class MockOnInterestHandler {
159
160 Name prefix;
161 OnInterest onInterest;
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700162 OnInterestCallback onInterestCallback;
Andrew Brown3831baf2015-01-19 13:38:52 -0800163 ForwardingFlags flags;
164
165 public MockOnInterestHandler(Name prefix, OnInterest onInterest, ForwardingFlags flags) {
166 this.prefix = prefix;
167 this.onInterest = onInterest;
168 this.flags = flags;
169 }
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700170
171 public MockOnInterestHandler(Name prefix, OnInterestCallback onInterestCallback, ForwardingFlags flags) {
172 this.prefix = prefix;
173 this.onInterestCallback = onInterestCallback;
174 this.flags = flags;
175 }
176
177 public void signal(Interest interest, long registeredPrefixId){
178 if(onInterest != null){
179 onInterest.onInterest(prefix, interest, node_.getTransport(), registeredPrefixId);
180 }
181 if(onInterestCallback != null){
182 onInterestCallback.onInterest(prefix, interest, MockFace.this, registeredPrefixId, null);
183 }
184 }
Andrew Brown3831baf2015-01-19 13:38:52 -0800185 }
186
187 /**
188 * Send the Interest through the transport, read the entire response and call
189 * onData(interest, data).
190 *
191 * @param interest The Interest to send. This copies the Interest.
192 * @param onData When a matching data packet is received, this calls
193 * onData.onData(interest, data) where interest is the interest given to
194 * expressInterest and data is the received Data object. NOTE: You must not
195 * change the interest object - if you need to change it then make a copy.
196 * @param onTimeout If the interest times out according to the interest
197 * lifetime, this calls onTimeout.onTimeout(interest) where interest is the
198 * interest given to expressInterest. If onTimeout is null, this does not use
199 * it.
200 * @param wireFormat A WireFormat object used to encode the message.
201 * @return The pending interest ID which can be used with
202 * removePendingInterest.
203 * @throws IOException For I/O error in sending the interest.
204 */
Andrew Brownb91e6902015-02-12 09:01:50 -0800205 @Override
Andrew Brown3831baf2015-01-19 13:38:52 -0800206 public long expressInterest(Interest interest, OnData onData, OnTimeout onTimeout,
207 WireFormat wireFormat) throws IOException {
andrewsbrown2304a342015-09-16 20:09:53 +0100208 long id = lastPendingInterestId++;
209 node_.expressInterest(id, interest, onData, onTimeout, wireFormat, this);
Andrew Brown3831baf2015-01-19 13:38:52 -0800210 handleIncomingRequests(interest);
211 return id;
212 }
213
214 /**
Andrew Brown3831baf2015-01-19 13:38:52 -0800215 * Register prefix with the connected NDN hub and call onInterest when a
216 * matching interest is received. If you have not called
217 * setCommandSigningInfo, this assumes you are connecting to NDNx. If you have
218 * called setCommandSigningInfo, this first sends an NFD registration request,
219 * and if that times out then this sends an NDNx registration request. If you
220 * need to register a prefix with NFD, you must first call
221 * setCommandSigningInfo.
222 *
223 * @param prefix A Name for the prefix to register. This copies the Name.
224 * @param onInterest When an interest is received which matches the name
225 * prefix, this calls onInterest.onInterest(prefix, interest, transport,
226 * registeredPrefixId). NOTE: You must not change the prefix object - if you
227 * need to change it then make a copy.
228 * @param onRegisterFailed If register prefix fails for any reason, this calls
229 * onRegisterFailed.onRegisterFailed(prefix).
230 * @param flags The flags for finer control of which interests are forwarded
231 * to the application.
232 * @param wireFormat A WireFormat object used to encode the message.
andrewsbrown2304a342015-09-16 20:09:53 +0100233 * @return The lastRegisteredPrefixId prefix ID which can be used with
234 removeRegisteredPrefix.
Andrew Brown3831baf2015-01-19 13:38:52 -0800235 * @throws IOException For I/O error in sending the registration request.
236 * @throws SecurityException If signing a command interest for NFD and cannot
237 * find the private key for the certificateName.
238 */
Andrew Brownb91e6902015-02-12 09:01:50 -0800239 @Override
Andrew Brown3831baf2015-01-19 13:38:52 -0800240 public long registerPrefix(Name prefix, OnInterest onInterest, OnRegisterFailed onRegisterFailed,
241 ForwardingFlags flags, WireFormat wireFormat) throws IOException, net.named_data.jndn.security.SecurityException {
andrewsbrowna52bf7d2015-04-06 13:51:53 -0700242 // since we don't send an Interest, ensure the transport is connected
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700243 if (!getTransport().getIsConnected()) {
andrewsbrown2304a342015-09-16 20:09:53 +0100244 getTransport().connect(node_.getConnectionInfo(), node_, null);
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700245 }
246
andrewsbrown2304a342015-09-16 20:09:53 +0100247 lastRegisteredPrefixId++;
248 handlerMap.put(lastRegisteredPrefixId, new MockOnInterestHandler(prefix, onInterest, flags));
249 return lastRegisteredPrefixId;
Andrew Brown3831baf2015-01-19 13:38:52 -0800250 }
251
Andrew Brownb91e6902015-02-12 09:01:50 -0800252 @Override
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700253 public long registerPrefix(Name prefix, OnInterestCallback onInterest, OnRegisterFailed onRegisterFailed, ForwardingFlags flags, WireFormat wireFormat) throws IOException, SecurityException {
254 // since we don't send an Interest, ensure the transport is connected
255 if (!getTransport().getIsConnected()) {
andrewsbrown2304a342015-09-16 20:09:53 +0100256 getTransport().connect(node_.getConnectionInfo(), node_, null);
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700257 }
258
andrewsbrown2304a342015-09-16 20:09:53 +0100259 lastRegisteredPrefixId++;
260 handlerMap.put(lastRegisteredPrefixId, new MockOnInterestHandler(prefix, onInterest, flags));
261 return lastRegisteredPrefixId;
Andrew Brown3831baf2015-01-19 13:38:52 -0800262 }
263
264 /**
Andrew Brown3831baf2015-01-19 13:38:52 -0800265 * Process any packets to receive and call callbacks such as onData,
266 * onInterest or onTimeout. This returns immediately if there is no data to
267 * receive. This blocks while calling the callbacks. You should repeatedly
268 * call this from an event loop, with calls to sleep as needed so that the
269 * loop doesn’t use 100% of the CPU. Since processEvents modifies the pending
270 * interest table, your application should make sure that it calls
271 * processEvents in the same thread as expressInterest (which also modifies
272 * the pending interest table). This may throw an exception for reading data
273 * or in the callback for processing the data. If you call this from an main
274 * event loop, you may want to catch and log/disregard all exceptions.
275 */
Andrew Brownb91e6902015-02-12 09:01:50 -0800276 @Override
Andrew Brown3831baf2015-01-19 13:38:52 -0800277 public void processEvents() throws IOException, EncodingException {
278 // Just call Node's processEvents.
279 node_.processEvents();
280 }
281
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700282 @Override
283 public void removeRegisteredPrefix(long registeredPrefixId) {
284 handlerMap.remove(registeredPrefixId);
285 }
286
287 @Override
288 public long setInterestFilter(InterestFilter filter, OnInterestCallback onInterest) {
andrewsbrown2304a342015-09-16 20:09:53 +0100289 long id = lastInterestFilterId++;
290 node_.setInterestFilter(id, filter, onInterest, this);
291 return id;
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700292 }
293
294 @Override
295 public void unsetInterestFilter(long interestFilterId) {
andrewsbrown0f36eee2015-05-07 01:37:48 +0100296 node_.unsetInterestFilter(interestFilterId);
andrewsbrown1f28bcf2015-04-20 13:29:20 -0700297 }
298
299 @Override
300 public void putData(Data data, WireFormat wireFormat) throws IOException {
301 node_.putData(data, wireFormat);
302 }
303
304 @Override
305 public void send(ByteBuffer encoding) throws IOException {
306 node_.send(encoding);
307 }
308
309 @Override
310 public boolean isLocal() throws IOException {
311 return true;
312 }
313
314 @Override
315 public void shutdown() {
316 node_.shutdown();
317 }
318
Andrew Brown3831baf2015-01-19 13:38:52 -0800319}