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.model.DiscoveryOptions;
19 import org.fourthline.cling.model.resource.Resource;
20 import org.fourthline.cling.model.gena.CancelReason;
21 import org.fourthline.cling.model.gena.LocalGENASubscription;
22 import org.fourthline.cling.model.meta.LocalDevice;
23 import org.fourthline.cling.model.types.UDN;
24 import org.fourthline.cling.protocol.SendingAsync;
25
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.Random;
33 import java.util.Set;
34 import java.util.logging.Logger;
35
36
37
38
39
40
41 class LocalItems extends RegistryItems<LocalDevice, LocalGENASubscription> {
42
43 private static Logger log = Logger.getLogger(Registry.class.getName());
44
45 protected Map<UDN, DiscoveryOptions> discoveryOptions = new HashMap<>();
46 protected long lastAliveIntervalTimestamp = 0;
47
48 LocalItems(RegistryImpl registry) {
49 super(registry);
50 }
51
52 protected void setDiscoveryOptions(UDN udn, DiscoveryOptions options) {
53 if (options != null)
54 this.discoveryOptions.put(udn, options);
55 else
56 this.discoveryOptions.remove(udn);
57 }
58
59 protected DiscoveryOptions getDiscoveryOptions(UDN udn) {
60 return this.discoveryOptions.get(udn);
61 }
62
63 protected boolean isAdvertised(UDN udn) {
64
65 return getDiscoveryOptions(udn) == null || getDiscoveryOptions(udn).isAdvertised();
66 }
67
68 protected boolean isByeByeBeforeFirstAlive(UDN udn) {
69
70 return getDiscoveryOptions(udn) != null && getDiscoveryOptions(udn).isByeByeBeforeFirstAlive();
71 }
72
73 void add(LocalDevice localDevice) throws RegistrationException {
74 add(localDevice, null);
75 }
76
77 void add(final LocalDevice localDevice, DiscoveryOptions options) throws RegistrationException {
78
79
80 setDiscoveryOptions(localDevice.getIdentity().getUdn(), options);
81
82 if (registry.getDevice(localDevice.getIdentity().getUdn(), false) != null) {
83 log.fine("Ignoring addition, device already registered: " + localDevice);
84 return;
85 }
86
87 log.fine("Adding local device to registry: " + localDevice);
88
89 for (Resource deviceResource : getResources(localDevice)) {
90
91 if (registry.getResource(deviceResource.getPathQuery()) != null) {
92 throw new RegistrationException("URI namespace conflict with already registered resource: " + deviceResource);
93 }
94
95 registry.addResource(deviceResource);
96 log.fine("Registered resource: " + deviceResource);
97
98 }
99
100 log.fine("Adding item to registry with expiration in seconds: " + localDevice.getIdentity().getMaxAgeSeconds());
101
102 RegistryItem<UDN, LocalDevice> localItem = new RegistryItem<>(
103 localDevice.getIdentity().getUdn(),
104 localDevice,
105 localDevice.getIdentity().getMaxAgeSeconds()
106 );
107
108 getDeviceItems().add(localItem);
109 log.fine("Registered local device: " + localItem);
110
111 if (isByeByeBeforeFirstAlive(localItem.getKey()))
112 advertiseByebye(localDevice, true);
113
114 if (isAdvertised(localItem.getKey()))
115 advertiseAlive(localDevice);
116
117 for (final RegistryListener listener : registry.getListeners()) {
118 registry.getConfiguration().getRegistryListenerExecutor().execute(
119 new Runnable() {
120 public void run() {
121 listener.localDeviceAdded(registry, localDevice);
122 }
123 }
124 );
125 }
126
127 }
128
129 Collection<LocalDevice> get() {
130 Set<LocalDevice> c = new HashSet<>();
131 for (RegistryItem<UDN, LocalDevice> item : getDeviceItems()) {
132 c.add(item.getItem());
133 }
134 return Collections.unmodifiableCollection(c);
135 }
136
137 boolean remove(final LocalDevice localDevice) throws RegistrationException {
138 return remove(localDevice, false);
139 }
140
141 boolean remove(final LocalDevice localDevice, boolean shuttingDown) throws RegistrationException {
142
143 LocalDevice registeredDevice = get(localDevice.getIdentity().getUdn(), true);
144 if (registeredDevice != null) {
145
146 log.fine("Removing local device from registry: " + localDevice);
147
148 setDiscoveryOptions(localDevice.getIdentity().getUdn(), null);
149 getDeviceItems().remove(new RegistryItem(localDevice.getIdentity().getUdn()));
150
151 for (Resource deviceResource : getResources(localDevice)) {
152 if (registry.removeResource(deviceResource)) {
153 log.fine("Unregistered resource: " + deviceResource);
154 }
155 }
156
157
158 Iterator<RegistryItem<String, LocalGENASubscription>> it = getSubscriptionItems().iterator();
159 while (it.hasNext()) {
160 final RegistryItem<String, LocalGENASubscription> incomingSubscription = it.next();
161
162 UDN subscriptionForUDN =
163 incomingSubscription.getItem().getService().getDevice().getIdentity().getUdn();
164
165 if (subscriptionForUDN.equals(registeredDevice.getIdentity().getUdn())) {
166 log.fine("Removing incoming subscription: " + incomingSubscription.getKey());
167 it.remove();
168 if (!shuttingDown) {
169 registry.getConfiguration().getRegistryListenerExecutor().execute(
170 new Runnable() {
171 public void run() {
172 incomingSubscription.getItem().end(CancelReason.DEVICE_WAS_REMOVED);
173 }
174 }
175 );
176 }
177 }
178 }
179
180 if (isAdvertised(localDevice.getIdentity().getUdn()))
181 advertiseByebye(localDevice, !shuttingDown);
182
183 if (!shuttingDown) {
184 for (final RegistryListener listener : registry.getListeners()) {
185 registry.getConfiguration().getRegistryListenerExecutor().execute(
186 new Runnable() {
187 public void run() {
188 listener.localDeviceRemoved(registry, localDevice);
189 }
190 }
191 );
192 }
193 }
194
195 return true;
196 }
197
198 return false;
199 }
200
201 void removeAll() {
202 removeAll(false);
203 }
204
205 void removeAll(boolean shuttingDown) {
206 LocalDevice[] allDevices = get().toArray(new LocalDevice[get().size()]);
207 for (LocalDevice device : allDevices) {
208 remove(device, shuttingDown);
209 }
210 }
211
212
213
214 public void advertiseLocalDevices() {
215 for (RegistryItem<UDN, LocalDevice> localItem : deviceItems) {
216 if (isAdvertised(localItem.getKey()))
217 advertiseAlive(localItem.getItem());
218 }
219 }
220
221
222
223 void maintain() {
224
225 if(getDeviceItems().isEmpty()) return ;
226
227 Set<RegistryItem<UDN, LocalDevice>> expiredLocalItems = new HashSet<>();
228
229
230 int aliveIntervalMillis = registry.getConfiguration().getAliveIntervalMillis();
231 if(aliveIntervalMillis > 0) {
232 long now = System.currentTimeMillis();
233 if(now - lastAliveIntervalTimestamp > aliveIntervalMillis) {
234 lastAliveIntervalTimestamp = now;
235 for (RegistryItem<UDN, LocalDevice> localItem : getDeviceItems()) {
236 if (isAdvertised(localItem.getKey())) {
237 log.finer("Flooding advertisement of local item: " + localItem);
238 expiredLocalItems.add(localItem);
239 }
240 }
241 }
242 } else {
243
244 lastAliveIntervalTimestamp = 0;
245
246
247 for (RegistryItem<UDN, LocalDevice> localItem : getDeviceItems()) {
248 if (isAdvertised(localItem.getKey()) && localItem.getExpirationDetails().hasExpired(true)) {
249 log.finer("Local item has expired: " + localItem);
250 expiredLocalItems.add(localItem);
251 }
252 }
253 }
254
255
256 for (RegistryItem<UDN, LocalDevice> expiredLocalItem : expiredLocalItems) {
257 log.fine("Refreshing local device advertisement: " + expiredLocalItem.getItem());
258 advertiseAlive(expiredLocalItem.getItem());
259 expiredLocalItem.getExpirationDetails().stampLastRefresh();
260 }
261
262
263 Set<RegistryItem<String, LocalGENASubscription>> expiredIncomingSubscriptions = new HashSet<>();
264 for (RegistryItem<String, LocalGENASubscription> item : getSubscriptionItems()) {
265 if (item.getExpirationDetails().hasExpired(false)) {
266 expiredIncomingSubscriptions.add(item);
267 }
268 }
269 for (RegistryItem<String, LocalGENASubscription> subscription : expiredIncomingSubscriptions) {
270 log.fine("Removing expired: " + subscription);
271 removeSubscription(subscription.getItem());
272 subscription.getItem().end(CancelReason.EXPIRED);
273 }
274
275 }
276
277 void shutdown() {
278 log.fine("Clearing all registered subscriptions to local devices during shutdown");
279 getSubscriptionItems().clear();
280
281 log.fine("Removing all local devices from registry during shutdown");
282 removeAll(true);
283 }
284
285
286
287 protected Random randomGenerator = new Random();
288
289 protected void advertiseAlive(final LocalDevice localDevice) {
290 registry.executeAsyncProtocol(new Runnable() {
291 public void run() {
292 try {
293 log.finer("Sleeping some milliseconds to avoid flooding the network with ALIVE msgs");
294 Thread.sleep(randomGenerator.nextInt(100));
295 } catch (InterruptedException ex) {
296 log.severe("Background execution interrupted: " + ex.getMessage());
297 }
298 registry.getProtocolFactory().createSendingNotificationAlive(localDevice).run();
299 }
300 });
301 }
302
303 protected void advertiseByebye(final LocalDevice localDevice, boolean asynchronous) {
304 final SendingAsync prot = registry.getProtocolFactory().createSendingNotificationByebye(localDevice);
305 if (asynchronous) {
306 registry.executeAsyncProtocol(prot);
307 } else {
308 prot.run();
309 }
310 }
311
312 }