blob: f13f9bd803ed3638285392a4f930cb78f52411b7 [file] [log] [blame]
Andrew Browna450fad2015-01-22 11:24:40 -08001/*
2 * File name: Server.java
3 *
4 * Purpose: Provide a server to simplify serving data over the NDN
5 * network.
6 *
7 * © Copyright Intel Corporation. All rights reserved.
8 * Intel Corporation, 2200 Mission College Boulevard,
9 * Santa Clara, CA 95052-8119, USA
10 */
11package com.intel.jndn.utils;
12
13import java.io.IOException;
14import net.named_data.jndn.Data;
15import net.named_data.jndn.Face;
16import net.named_data.jndn.ForwardingFlags;
17import net.named_data.jndn.Interest;
18import net.named_data.jndn.Name;
19import net.named_data.jndn.OnInterest;
20import net.named_data.jndn.OnRegisterFailed;
21import net.named_data.jndn.encoding.EncodingException;
Andrew Browna88e6ef2015-01-22 11:43:44 -080022import net.named_data.jndn.security.KeyChain;
Andrew Browna450fad2015-01-22 11:24:40 -080023import net.named_data.jndn.transport.Transport;
24import org.apache.logging.log4j.LogManager;
25import org.apache.logging.log4j.Logger;
26
27/**
28 * Provide a server to simplify serving data over the NDN network. Exposes two
29 * main methods: put() for serving static, known data packets and on() for
30 * serving dynamically created packets on-demand.
31 *
32 * @author Andrew Brown <andrew.brown@intel.com>
33 */
34public class Server {
35
36 public static final long DEFAULT_SLEEP_TIME = 20;
37 public static final long DEFAULT_TIMEOUT = 2000;
38 private static final Logger logger = LogManager.getLogger();
39 private static Server defaultInstance;
Andrew Browna88e6ef2015-01-22 11:43:44 -080040 private KeyChain keyChain;
41 private Name certificateName;
Andrew Browna450fad2015-01-22 11:24:40 -080042
43 /**
44 * Singleton access for simpler server use
45 *
46 * @return
47 */
48 public static Server getDefault() {
49 if (defaultInstance == null) {
50 defaultInstance = new Server();
51 }
52 return defaultInstance;
53 }
54
55 /**
Andrew Browna88e6ef2015-01-22 11:43:44 -080056 * Constructor
57 */
58 public Server() {
59 // no signing
60 }
61
62 /**
63 * Constructor; enables signing
64 *
65 * @param keyChain
66 * @param certificateName
67 */
68 public Server(KeyChain keyChain, Name certificateName) {
69 this.keyChain = keyChain;
70 this.certificateName = certificateName;
71 }
72
73 /**
Andrew Browna450fad2015-01-22 11:24:40 -080074 * Synchronously serve a Data on the given face until one request accesses the
75 * data; will return incoming Interest request.
76 * <pre><code> Interest request = Client.putSync(face, data); </code></pre>
77 *
78 * @param face
79 * @param data
80 * @return
81 */
82 public Interest putSync(Face face, final Data data) {
83 // setup event
84 long startTime = System.currentTimeMillis();
85 final String dataName = data.getName().toUri();
86 final NDNEvent event = new NDNEvent();
87
88 // setup flags
89 ForwardingFlags flags = new ForwardingFlags();
90 flags.setCapture(true);
91
92 // register the data name on the face
93 try {
94 face.registerPrefix(data.getName(), new OnInterest() {
95 @Override
96 public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
Andrew Browna88e6ef2015-01-22 11:43:44 -080097 // sign packet
98 if (keyChain != null) {
99 try {
100 keyChain.sign(data, certificateName != null ? certificateName : keyChain.getDefaultCertificateName());
101 } catch (net.named_data.jndn.security.SecurityException e) {
102 logger.error("Failed to sign data for: " + dataName, e);
103 event.fromPacket(e);
104 }
105 }
106
107 // send packet
Andrew Browna450fad2015-01-22 11:24:40 -0800108 try {
109 transport.send(data.wireEncode().buf());
110 logger.debug("Sent data: " + dataName);
111 event.fromPacket(interest);
112 } catch (IOException e) {
113 logger.error("Failed to send data for: " + dataName);
114 event.fromPacket(e);
115 }
116 }
117 }, new OnRegisterFailed() {
118 @Override
119 public void onRegisterFailed(Name prefix) {
120 event.fromPacket(new Exception("Failed to register name: " + dataName));
121 }
122 }, flags);
123 logger.info("Registered data: " + dataName);
124 } catch (IOException e) {
125 logger.error("Could not connect to face to register prefix: " + dataName, e);
126 event.fromPacket(e);
127 } catch (net.named_data.jndn.security.SecurityException e) {
128 logger.error("Error registering prefix: " + dataName, e);
129 event.fromPacket(e);
130 }
131
132 // process eventCount until one response is sent or error
133 while (event.getPacket() == null) {
134 try {
135 synchronized (face) {
136 face.processEvents();
137 }
138 } catch (IOException | EncodingException e) {
139 logger.warn("Failed to process events.", e);
140 event.fromPacket(e);
141 }
142 sleep();
143 }
144
145 // return
146 logger.debug("Request time (ms): " + (event.getTimestamp() - startTime));
147 return (event.isSuccess()) ? (Interest) event.getPacket() : null;
148 }
149
150 /**
151 * Asynchronously serve a Data on the given face until an observer stops it.
152 * E.g.: NDNObserver observer = Client.put(face, data); // when finished
Andrew Browna88e6ef2015-01-22 11:43:44 -0800153 * serving the data, stop the background thread observer.stop();
Andrew Browna450fad2015-01-22 11:24:40 -0800154 *
155 * @param face
156 * @param data
157 * @return
158 */
159 public NDNObserver put(final Face face, final Data data) {
160 // setup observer
161 final NDNObserver observer = new NDNObserver();
162 final NDNObservable eventHandler = new NDNObservable();
163 eventHandler.addObserver(observer);
164
Andrew Browna88e6ef2015-01-22 11:43:44 -0800165 // setup interest handler
Andrew Browna450fad2015-01-22 11:24:40 -0800166 final OnInterest interestHandler = new OnInterest() {
167 @Override
168 public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
Andrew Browna88e6ef2015-01-22 11:43:44 -0800169 // sign packet
170 if (keyChain != null) {
171 try {
172 keyChain.sign(data, certificateName != null ? certificateName : keyChain.getDefaultCertificateName());
173 } catch (net.named_data.jndn.security.SecurityException e) {
174 logger.error("Failed to sign data for: " + data.getName().toUri(), e);
175 eventHandler.notify(e);
176 }
177 }
178
179 // send packet
Andrew Browna450fad2015-01-22 11:24:40 -0800180 try {
181 transport.send(data.wireEncode().buf());
182 } catch (IOException e) {
183 logger.error("Failed to send data for: " + data.getName().toUri());
184 eventHandler.notify(e);
185 }
186 }
187 };
Andrew Browna88e6ef2015-01-22 11:43:44 -0800188
189 // setup failure handler
Andrew Browna450fad2015-01-22 11:24:40 -0800190 final OnRegisterFailed failureHandler = new OnRegisterFailed() {
191 @Override
192 public void onRegisterFailed(Name prefix) {
193 logger.error("Failed to register name to put: " + data.getName().toUri());
194 eventHandler.notify(new Exception("Failed to register name to put: " + data.getName().toUri()));
195 }
196 };
Andrew Browna88e6ef2015-01-22 11:43:44 -0800197
198 // setup forwarding flags
Andrew Browna450fad2015-01-22 11:24:40 -0800199 final ForwardingFlags flags = new ForwardingFlags();
200 flags.setCapture(true); // no shorter routes will answer for this prefix, see http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Route-inheritance
201 flags.setChildInherit(false); // the interest name must be exact, no child components after the prefix
202
Andrew Browna88e6ef2015-01-22 11:43:44 -0800203 // start background thread
Andrew Browna450fad2015-01-22 11:24:40 -0800204 Thread backgroundThread = new Thread(new Runnable() {
205 @Override
206 public void run() {
207 // register name on the face
208 try {
209 face.registerPrefix(data.getName(), interestHandler, failureHandler, flags);
210 logger.info("Registered data : " + data.getName().toUri());
211 } catch (IOException e) {
212 logger.error("Could not connect to face to register prefix: " + data.getName().toUri(), e);
213 eventHandler.notify(e);
214 } catch (net.named_data.jndn.security.SecurityException e) {
215 logger.error("Error registering prefix: " + data.getName().toUri(), e);
216 eventHandler.notify(e);
217 }
218
219 // process eventCount until a request is received
220 while (observer.interestCount() == 0 && observer.errorCount() == 0 && !observer.mustStop()) {
221 try {
222 synchronized (face) {
223 face.processEvents();
224 }
225 } catch (IOException | EncodingException e) {
226 logger.warn("Failed to process events.", e);
227 eventHandler.notify(e);
228 }
229 sleep();
230 }
231 }
232 });
233 backgroundThread.setName(String.format("Server.put(%s)", data.getName().toUri()));
234 backgroundThread.setDaemon(true);
235 backgroundThread.start();
236
237 return observer;
238 }
239
240 /**
241 * Register a prefix on the face to serve Data packets for incoming Interests.
Andrew Browna88e6ef2015-01-22 11:43:44 -0800242 * This method will create a background thread to process events until the
243 * user calls stop() on the returned observer
244 *
Andrew Browna450fad2015-01-22 11:24:40 -0800245 * @param face
246 * @param prefix
247 * @param handler
Andrew Browna88e6ef2015-01-22 11:43:44 -0800248 * @return
Andrew Browna450fad2015-01-22 11:24:40 -0800249 */
250 public NDNObserver on(final Face face, final Name prefix, final OnServeInterest handler) {
251 // setup observer
252 final NDNObserver observer = new NDNObserver();
253 final NDNObservable eventHandler = new NDNObservable();
254 eventHandler.addObserver(observer);
255
Andrew Browna88e6ef2015-01-22 11:43:44 -0800256 // setup interest handler
Andrew Browna450fad2015-01-22 11:24:40 -0800257 final OnInterest interestHandler = new OnInterest() {
258 @Override
259 public void onInterest(Name prefix, Interest interest, Transport transport, long registeredPrefixId) {
Andrew Browna88e6ef2015-01-22 11:43:44 -0800260 // notify observers of interest received
Andrew Browna450fad2015-01-22 11:24:40 -0800261 eventHandler.notify(interest);
Andrew Browna88e6ef2015-01-22 11:43:44 -0800262
263 // grab data from OnServeInterest handler
264 Data data = handler.onInterest(prefix, interest);
265
266 // sign packet
267 if (keyChain != null) {
268 try {
269 keyChain.sign(data, certificateName != null ? certificateName : keyChain.getDefaultCertificateName());
270 } catch (net.named_data.jndn.security.SecurityException e) {
271 logger.error("Failed to sign data for: " + interest.getName().toUri(), e);
272 eventHandler.notify(e);
273 }
274 }
275
276 // send packet
Andrew Browna450fad2015-01-22 11:24:40 -0800277 try {
Andrew Browna450fad2015-01-22 11:24:40 -0800278 transport.send(data.wireEncode().buf());
Andrew Browna88e6ef2015-01-22 11:43:44 -0800279 eventHandler.notify(data); // notify observers of data sent
Andrew Browna450fad2015-01-22 11:24:40 -0800280 } catch (IOException e) {
Andrew Browna88e6ef2015-01-22 11:43:44 -0800281 logger.error("Failed to send data for: " + interest.getName().toUri());
Andrew Browna450fad2015-01-22 11:24:40 -0800282 eventHandler.notify(e);
283 }
284 }
285 };
Andrew Browna88e6ef2015-01-22 11:43:44 -0800286
287 // setup failure handler
Andrew Browna450fad2015-01-22 11:24:40 -0800288 final OnRegisterFailed failureHandler = new OnRegisterFailed() {
289 @Override
290 public void onRegisterFailed(Name prefix) {
291 logger.error("Failed to register name to put: " + prefix.toUri());
292 eventHandler.notify(new Exception("Failed to register name to put: " + prefix.toUri()));
293 }
294 };
Andrew Browna88e6ef2015-01-22 11:43:44 -0800295
296 // setup forwarding flags
Andrew Browna450fad2015-01-22 11:24:40 -0800297 final ForwardingFlags flags = new ForwardingFlags();
298 flags.setCapture(true); // no shorter routes will answer for this prefix, see http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Route-inheritance
299 flags.setChildInherit(true); // the interest name may have child components after the prefix
300
Andrew Browna88e6ef2015-01-22 11:43:44 -0800301 // start background thread
Andrew Browna450fad2015-01-22 11:24:40 -0800302 Thread backgroundThread = new Thread(new Runnable() {
303 @Override
304 public void run() {
305 // register name on the face
306 try {
307 face.registerPrefix(prefix, interestHandler, failureHandler, flags);
308 logger.info("Registered data : " + prefix.toUri());
309 } catch (IOException e) {
310 logger.error("Could not connect to face to register prefix: " + prefix.toUri(), e);
311 eventHandler.notify(e);
312 } catch (net.named_data.jndn.security.SecurityException e) {
313 logger.error("Error registering prefix: " + prefix.toUri(), e);
314 eventHandler.notify(e);
315 }
316
317 // process events until told to stop or error bubbles up
318 while (observer.errorCount() == 0 && !observer.mustStop()) {
319 try {
320 synchronized (face) {
321 face.processEvents();
322 }
323 } catch (IOException | EncodingException e) {
324 logger.warn("Failed to process events.", e);
325 eventHandler.notify(e);
326 }
327 sleep();
328 }
329 }
330 });
331 backgroundThread.setName(String.format("Client.put(%s)", prefix.toUri()));
332 backgroundThread.setDaemon(true);
333 backgroundThread.start();
334
335 return observer;
336 }
337
338 /**
339 * Put the current thread to sleep to allow time for IO
340 */
341 protected void sleep() {
342 try {
343 Thread.currentThread().sleep(DEFAULT_SLEEP_TIME);
344 } catch (InterruptedException e) {
345 logger.error("Event loop interrupted.", e);
346 }
347 }
348
349}