1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.registry;
17
18 import org.fourthline.cling.UpnpService;
19 import org.fourthline.cling.UpnpServiceConfiguration;
20 import org.fourthline.cling.model.DiscoveryOptions;
21 import org.fourthline.cling.model.ExpirationDetails;
22 import org.fourthline.cling.model.ServiceReference;
23 import org.fourthline.cling.model.gena.LocalGENASubscription;
24 import org.fourthline.cling.model.gena.RemoteGENASubscription;
25 import org.fourthline.cling.model.meta.Device;
26 import org.fourthline.cling.model.meta.LocalDevice;
27 import org.fourthline.cling.model.meta.RemoteDevice;
28 import org.fourthline.cling.model.meta.RemoteDeviceIdentity;
29 import org.fourthline.cling.model.meta.Service;
30 import org.fourthline.cling.model.resource.Resource;
31 import org.fourthline.cling.model.types.DeviceType;
32 import org.fourthline.cling.model.types.ServiceType;
33 import org.fourthline.cling.model.types.UDN;
34 import org.fourthline.cling.protocol.ProtocolFactory;
35
36 import javax.enterprise.context.ApplicationScoped;
37 import javax.inject.Inject;
38 import java.net.URI;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.Set;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48
49
50
51
52
53
54 @ApplicationScoped
55 public class RegistryImpl implements Registry {
56
57 private static Logger log = Logger.getLogger(Registry.class.getName());
58
59 protected UpnpService upnpService;
60 protected RegistryMaintainer registryMaintainer;
61 protected final Set<RemoteGENASubscription> pendingSubscriptionsLock = new HashSet<>();
62
63 public RegistryImpl() {
64 }
65
66
67
68
69 @Inject
70 public RegistryImpl(UpnpService upnpService) {
71 log.fine("Creating Registry: " + getClass().getName());
72
73 this.upnpService = upnpService;
74
75 log.fine("Starting registry background maintenance...");
76 registryMaintainer = createRegistryMaintainer();
77 if (registryMaintainer != null) {
78 getConfiguration().getRegistryMaintainerExecutor().execute(registryMaintainer);
79 }
80 }
81
82 public UpnpService getUpnpService() {
83 return upnpService;
84 }
85
86 public UpnpServiceConfiguration getConfiguration() {
87 return getUpnpService().getConfiguration();
88 }
89
90 public ProtocolFactory getProtocolFactory() {
91 return getUpnpService().getProtocolFactory();
92 }
93
94 protected RegistryMaintainer createRegistryMaintainer() {
95 return new RegistryMaintainer(
96 this,
97 getConfiguration().getRegistryMaintenanceIntervalMillis()
98 );
99 }
100
101
102
103 protected final Set<RegistryListener> registryListeners = new HashSet<>();
104 protected final Set<RegistryItem<URI, Resource>> resourceItems = new HashSet<>();
105 protected final List<Runnable> pendingExecutions = new ArrayList<>();
106
107 protected final RemoteItems remoteItems = new RemoteItems(this);
108 protected final LocalItems localItems = new LocalItems(this);
109
110
111
112 synchronized public void addListener(RegistryListener listener) {
113 registryListeners.add(listener);
114 }
115
116 synchronized public void removeListener(RegistryListener listener) {
117 registryListeners.remove(listener);
118 }
119
120 synchronized public Collection<RegistryListener> getListeners() {
121 return Collections.unmodifiableCollection(registryListeners);
122 }
123
124 synchronized public boolean notifyDiscoveryStart(final RemoteDevice device) {
125
126 if (getUpnpService().getRegistry().getRemoteDevice(device.getIdentity().getUdn(), true) != null) {
127 log.finer("Not notifying listeners, already registered: " + device);
128 return false;
129 }
130 for (final RegistryListener listener : getListeners()) {
131 getConfiguration().getRegistryListenerExecutor().execute(
132 new Runnable() {
133 public void run() {
134 listener.remoteDeviceDiscoveryStarted(RegistryImpl.this, device);
135 }
136 }
137 );
138 }
139 return true;
140 }
141
142 synchronized public void notifyDiscoveryFailure(final RemoteDevice device, final Exception ex) {
143 for (final RegistryListener listener : getListeners()) {
144 getConfiguration().getRegistryListenerExecutor().execute(
145 new Runnable() {
146 public void run() {
147 listener.remoteDeviceDiscoveryFailed(RegistryImpl.this, device, ex);
148 }
149 }
150 );
151 }
152 }
153
154
155
156 synchronized public void addDevice(LocalDevice localDevice) {
157 localItems.add(localDevice);
158 }
159
160 synchronized public void addDevice(LocalDevice localDevice, DiscoveryOptions options) {
161 localItems.add(localDevice, options);
162 }
163
164 synchronized public void setDiscoveryOptions(UDN udn, DiscoveryOptions options) {
165 localItems.setDiscoveryOptions(udn, options);
166 }
167
168 synchronized public DiscoveryOptions getDiscoveryOptions(UDN udn) {
169 return localItems.getDiscoveryOptions(udn);
170 }
171
172 synchronized public void addDevice(RemoteDevice remoteDevice) {
173 remoteItems.add(remoteDevice);
174 }
175
176 synchronized public boolean update(RemoteDeviceIdentity rdIdentity) {
177 return remoteItems.update(rdIdentity);
178 }
179
180 synchronized public boolean removeDevice(LocalDevice localDevice) {
181 return localItems.remove(localDevice);
182 }
183
184 synchronized public boolean removeDevice(RemoteDevice remoteDevice) {
185 return remoteItems.remove(remoteDevice);
186 }
187
188 synchronized public void removeAllLocalDevices() {
189 localItems.removeAll();
190 }
191
192 synchronized public void removeAllRemoteDevices() {
193 remoteItems.removeAll();
194 }
195
196 synchronized public boolean removeDevice(UDN udn) {
197 Device device = getDevice(udn, true);
198 if (device != null && device instanceof LocalDevice)
199 return removeDevice((LocalDevice) device);
200 if (device != null && device instanceof RemoteDevice)
201 return removeDevice((RemoteDevice) device);
202 return false;
203 }
204
205 synchronized public Device getDevice(UDN udn, boolean rootOnly) {
206 Device device;
207 if ((device = localItems.get(udn, rootOnly)) != null) return device;
208 if ((device = remoteItems.get(udn, rootOnly)) != null) return device;
209 return null;
210 }
211
212 synchronized public LocalDevice getLocalDevice(UDN udn, boolean rootOnly) {
213 return localItems.get(udn, rootOnly);
214 }
215
216 synchronized public RemoteDevice getRemoteDevice(UDN udn, boolean rootOnly) {
217 return remoteItems.get(udn, rootOnly);
218 }
219
220 synchronized public Collection<LocalDevice> getLocalDevices() {
221 return Collections.unmodifiableCollection(localItems.get());
222 }
223
224 synchronized public Collection<RemoteDevice> getRemoteDevices() {
225 return Collections.unmodifiableCollection(remoteItems.get());
226 }
227
228 synchronized public Collection<Device> getDevices() {
229 Set all = new HashSet<>();
230 all.addAll(localItems.get());
231 all.addAll(remoteItems.get());
232 return Collections.unmodifiableCollection(all);
233 }
234
235 synchronized public Collection<Device> getDevices(DeviceType deviceType) {
236 Collection<Device> devices = new HashSet<>();
237
238 devices.addAll(localItems.get(deviceType));
239 devices.addAll(remoteItems.get(deviceType));
240
241 return Collections.unmodifiableCollection(devices);
242 }
243
244 synchronized public Collection<Device> getDevices(ServiceType serviceType) {
245 Collection<Device> devices = new HashSet<>();
246
247 devices.addAll(localItems.get(serviceType));
248 devices.addAll(remoteItems.get(serviceType));
249
250 return Collections.unmodifiableCollection(devices);
251 }
252
253 synchronized public Service getService(ServiceReference serviceReference) {
254 Device device;
255 if ((device = getDevice(serviceReference.getUdn(), false)) != null) {
256 return device.findService(serviceReference.getServiceId());
257 }
258 return null;
259 }
260
261
262
263 synchronized public Resource getResource(URI pathQuery) throws IllegalArgumentException {
264 if (pathQuery.isAbsolute()) {
265 throw new IllegalArgumentException("Resource URI can not be absolute, only path and query:" + pathQuery);
266 }
267
268
269
270 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
271 Resource resource = resourceItem.getItem();
272 if (resource.matches(pathQuery)) {
273 return resource;
274 }
275 }
276
277
278
279 if (pathQuery.getPath().endsWith("/")) {
280 URI pathQueryWithoutSlash = URI.create(pathQuery.toString().substring(0, pathQuery.toString().length() - 1));
281
282 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
283 Resource resource = resourceItem.getItem();
284 if (resource.matches(pathQueryWithoutSlash)) {
285 return resource;
286 }
287 }
288 }
289
290 return null;
291 }
292
293 synchronized public <T extends Resource> T getResource(Class<T> resourceType, URI pathQuery) throws IllegalArgumentException {
294 Resource resource = getResource(pathQuery);
295 if (resource != null && resourceType.isAssignableFrom(resource.getClass())) {
296 return (T) resource;
297 }
298 return null;
299 }
300
301 synchronized public Collection<Resource> getResources() {
302 Collection<Resource> s = new HashSet<>();
303 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
304 s.add(resourceItem.getItem());
305 }
306 return s;
307 }
308
309 synchronized public <T extends Resource> Collection<T> getResources(Class<T> resourceType) {
310 Collection<T> s = new HashSet<>();
311 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
312 if (resourceType.isAssignableFrom(resourceItem.getItem().getClass()))
313 s.add((T) resourceItem.getItem());
314 }
315 return s;
316 }
317
318 synchronized public void addResource(Resource resource) {
319 addResource(resource, ExpirationDetails.UNLIMITED_AGE);
320 }
321
322 synchronized public void addResource(Resource resource, int maxAgeSeconds) {
323 RegistryItem resourceItem = new RegistryItem(resource.getPathQuery(), resource, maxAgeSeconds);
324 resourceItems.remove(resourceItem);
325 resourceItems.add(resourceItem);
326 }
327
328 synchronized public boolean removeResource(Resource resource) {
329 return resourceItems.remove(new RegistryItem(resource.getPathQuery()));
330 }
331
332
333
334 synchronized public void addLocalSubscription(LocalGENASubscription subscription) {
335 localItems.addSubscription(subscription);
336 }
337
338 synchronized public LocalGENASubscription getLocalSubscription(String subscriptionId) {
339 return localItems.getSubscription(subscriptionId);
340 }
341
342 synchronized public boolean updateLocalSubscription(LocalGENASubscription subscription) {
343 return localItems.updateSubscription(subscription);
344 }
345
346 synchronized public boolean removeLocalSubscription(LocalGENASubscription subscription) {
347 return localItems.removeSubscription(subscription);
348 }
349
350 synchronized public void addRemoteSubscription(RemoteGENASubscription subscription) {
351 remoteItems.addSubscription(subscription);
352 }
353
354 synchronized public RemoteGENASubscription getRemoteSubscription(String subscriptionId) {
355 return remoteItems.getSubscription(subscriptionId);
356 }
357
358 synchronized public void updateRemoteSubscription(RemoteGENASubscription subscription) {
359 remoteItems.updateSubscription(subscription);
360 }
361
362 synchronized public void removeRemoteSubscription(RemoteGENASubscription subscription) {
363 remoteItems.removeSubscription(subscription);
364 }
365
366
367
368 synchronized public void advertiseLocalDevices() {
369 localItems.advertiseLocalDevices();
370 }
371
372
373
374
375 synchronized public void shutdown() {
376 log.fine("Shutting down registry...");
377
378 if (registryMaintainer != null)
379 registryMaintainer.stop();
380
381
382
383 log.finest("Executing final pending operations on shutdown: " + pendingExecutions.size());
384 runPendingExecutions(false);
385
386 for (RegistryListener listener : registryListeners) {
387 listener.beforeShutdown(this);
388 }
389
390 RegistryItem<URI, Resource>[] resources = resourceItems.toArray(new RegistryItem[resourceItems.size()]);
391 for (RegistryItem<URI, Resource> resourceItem : resources) {
392 resourceItem.getItem().shutdown();
393 }
394
395 remoteItems.shutdown();
396 localItems.shutdown();
397
398 for (RegistryListener listener : registryListeners) {
399 listener.afterShutdown();
400 }
401 }
402
403 synchronized public void pause() {
404 if (registryMaintainer != null) {
405 log.fine("Pausing registry maintenance");
406 runPendingExecutions(true);
407 registryMaintainer.stop();
408 registryMaintainer = null;
409 }
410 }
411
412 synchronized public void resume() {
413 if (registryMaintainer == null) {
414 log.fine("Resuming registry maintenance");
415 remoteItems.resume();
416 registryMaintainer = createRegistryMaintainer();
417 if (registryMaintainer != null) {
418 getConfiguration().getRegistryMaintainerExecutor().execute(registryMaintainer);
419 }
420 }
421 }
422
423 synchronized public boolean isPaused() {
424 return registryMaintainer == null;
425 }
426
427
428
429 synchronized void maintain() {
430
431 if (log.isLoggable(Level.FINEST))
432 log.finest("Maintaining registry...");
433
434
435 Iterator<RegistryItem<URI, Resource>> it = resourceItems.iterator();
436 while (it.hasNext()) {
437 RegistryItem<URI, Resource> item = it.next();
438 if (item.getExpirationDetails().hasExpired()) {
439 if (log.isLoggable(Level.FINER))
440 log.finer("Removing expired resource: " + item);
441 it.remove();
442 }
443 }
444
445
446 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
447 resourceItem.getItem().maintain(
448 pendingExecutions,
449 resourceItem.getExpirationDetails()
450 );
451 }
452
453
454 remoteItems.maintain();
455 localItems.maintain();
456
457
458 runPendingExecutions(true);
459 }
460
461 synchronized void executeAsyncProtocol(Runnable runnable) {
462 pendingExecutions.add(runnable);
463 }
464
465 synchronized void runPendingExecutions(boolean async) {
466 if (log.isLoggable(Level.FINEST))
467 log.finest("Executing pending operations: " + pendingExecutions.size());
468 for (Runnable pendingExecution : pendingExecutions) {
469 if (async)
470 getConfiguration().getAsyncProtocolExecutor().execute(pendingExecution);
471 else
472 pendingExecution.run();
473 }
474 if (pendingExecutions.size() > 0) {
475 pendingExecutions.clear();
476 }
477 }
478
479
480
481 public void printDebugLog() {
482 if (log.isLoggable(Level.FINE)) {
483 log.fine("==================================== REMOTE ================================================");
484
485 for (RemoteDevice remoteDevice : remoteItems.get()) {
486 log.fine(remoteDevice.toString());
487 }
488
489 log.fine("==================================== LOCAL ================================================");
490
491 for (LocalDevice localDevice : localItems.get()) {
492 log.fine(localDevice.toString());
493 }
494
495 log.fine("==================================== RESOURCES ================================================");
496
497 for (RegistryItem<URI, Resource> resourceItem : resourceItems) {
498 log.fine(resourceItem.toString());
499 }
500
501 log.fine("=================================================================================================");
502
503 }
504
505 }
506
507 @Override
508 public void registerPendingRemoteSubscription(RemoteGENASubscription subscription) {
509 synchronized (pendingSubscriptionsLock) {
510 pendingSubscriptionsLock.add(subscription);
511 }
512 }
513
514 @Override
515 public void unregisterPendingRemoteSubscription(RemoteGENASubscription subscription) {
516 synchronized (pendingSubscriptionsLock) {
517 if(pendingSubscriptionsLock.remove(subscription)) {
518 pendingSubscriptionsLock.notifyAll();
519 }
520 }
521 }
522
523 @Override
524 public RemoteGENASubscription getWaitRemoteSubscription(String subscriptionId) {
525 synchronized (pendingSubscriptionsLock) {
526 RemoteGENASubscription subscription = getRemoteSubscription(subscriptionId);
527 while (subscription == null && !pendingSubscriptionsLock.isEmpty()) {
528 try {
529 log.finest("Subscription not found, waiting for pending subscription procedure to terminate.");
530 pendingSubscriptionsLock.wait();
531 } catch (InterruptedException e) {
532 }
533 subscription = getRemoteSubscription(subscriptionId);
534 }
535 return subscription;
536 }
537 }
538
539 }