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.resource.Resource;
19 import org.fourthline.cling.model.gena.CancelReason;
20 import org.fourthline.cling.model.gena.RemoteGENASubscription;
21 import org.fourthline.cling.model.meta.LocalDevice;
22 import org.fourthline.cling.model.meta.RemoteDevice;
23 import org.fourthline.cling.model.meta.RemoteDeviceIdentity;
24 import org.fourthline.cling.model.types.UDN;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36
37
38
39
40
41 class RemoteItems extends RegistryItems<RemoteDevice, RemoteGENASubscription> {
42
43 private static Logger log = Logger.getLogger(Registry.class.getName());
44
45 RemoteItems(RegistryImpl registry) {
46 super(registry);
47 }
48
49
50
51
52
53
54
55
56
57
58
59
60
61 void add(final RemoteDevice device) {
62
63 if (update(device.getIdentity())) {
64 log.fine("Ignoring addition, device already registered: " + device);
65 return;
66 }
67
68 Resource[] resources = getResources(device);
69
70 for (Resource deviceResource : resources) {
71 log.fine("Validating remote device resource; " + deviceResource);
72 if (registry.getResource(deviceResource.getPathQuery()) != null) {
73 throw new RegistrationException("URI namespace conflict with already registered resource: " + deviceResource);
74 }
75 }
76
77 for (Resource validatedResource : resources) {
78 registry.addResource(validatedResource);
79 log.fine("Added remote device resource: " + validatedResource);
80 }
81
82
83 RegistryItem item = new RegistryItem(
84 device.getIdentity().getUdn(),
85 device,
86 registry.getConfiguration().getRemoteDeviceMaxAgeSeconds() != null
87 ? registry.getConfiguration().getRemoteDeviceMaxAgeSeconds()
88 : device.getIdentity().getMaxAgeSeconds()
89 );
90 log.fine("Adding hydrated remote device to registry with "
91 + item.getExpirationDetails().getMaxAgeSeconds() + " seconds expiration: " + device);
92 getDeviceItems().add(item);
93
94 if (log.isLoggable(Level.FINEST)) {
95 StringBuilder sb = new StringBuilder();
96 sb.append("\n");
97 sb.append("-------------------------- START Registry Namespace -----------------------------------\n");
98 for (Resource resource : registry.getResources()) {
99 sb.append(resource).append("\n");
100 }
101 sb.append("-------------------------- END Registry Namespace -----------------------------------");
102 log.finest(sb.toString());
103 }
104
105
106 log.fine("Completely hydrated remote device graph available, calling listeners: " + device);
107 for (final RegistryListener listener : registry.getListeners()) {
108 registry.getConfiguration().getRegistryListenerExecutor().execute(
109 new Runnable() {
110 public void run() {
111 listener.remoteDeviceAdded(registry, device);
112 }
113 }
114 );
115 }
116
117 }
118
119 boolean update(RemoteDeviceIdentity rdIdentity) {
120
121 for (LocalDevice localDevice : registry.getLocalDevices()) {
122 if (localDevice.findDevice(rdIdentity.getUdn()) != null) {
123 log.fine("Ignoring update, a local device graph contains UDN");
124 return true;
125 }
126 }
127
128 RemoteDevice registeredRemoteDevice = get(rdIdentity.getUdn(), false);
129 if (registeredRemoteDevice != null) {
130
131 if (!registeredRemoteDevice.isRoot()) {
132 log.fine("Updating root device of embedded: " + registeredRemoteDevice);
133 registeredRemoteDevice = registeredRemoteDevice.getRoot();
134 }
135
136
137 final RegistryItem<UDN, RemoteDevice> item = new RegistryItem<>(
138 registeredRemoteDevice.getIdentity().getUdn(),
139 registeredRemoteDevice,
140 registry.getConfiguration().getRemoteDeviceMaxAgeSeconds() != null
141 ? registry.getConfiguration().getRemoteDeviceMaxAgeSeconds()
142 : rdIdentity.getMaxAgeSeconds()
143 );
144
145 log.fine("Updating expiration of: " + registeredRemoteDevice);
146 getDeviceItems().remove(item);
147 getDeviceItems().add(item);
148
149 log.fine("Remote device updated, calling listeners: " + registeredRemoteDevice);
150 for (final RegistryListener listener : registry.getListeners()) {
151 registry.getConfiguration().getRegistryListenerExecutor().execute(
152 new Runnable() {
153 public void run() {
154 listener.remoteDeviceUpdated(registry, item.getItem());
155 }
156 }
157 );
158 }
159
160 return true;
161
162 }
163 return false;
164 }
165
166
167
168
169
170
171
172 boolean remove(final RemoteDevice remoteDevice) {
173 return remove(remoteDevice, false);
174 }
175
176 boolean remove(final RemoteDevice remoteDevice, boolean shuttingDown) throws RegistrationException {
177 final RemoteDevice registeredDevice = get(remoteDevice.getIdentity().getUdn(), true);
178 if (registeredDevice != null) {
179
180 log.fine("Removing remote device from registry: " + remoteDevice);
181
182
183 for (Resource deviceResource : getResources(registeredDevice)) {
184 if (registry.removeResource(deviceResource)) {
185 log.fine("Unregistered resource: " + deviceResource);
186 }
187 }
188
189
190 Iterator<RegistryItem<String, RemoteGENASubscription>> it = getSubscriptionItems().iterator();
191 while (it.hasNext()) {
192 final RegistryItem<String, RemoteGENASubscription> outgoingSubscription = it.next();
193
194 UDN subscriptionForUDN =
195 outgoingSubscription.getItem().getService().getDevice().getIdentity().getUdn();
196
197 if (subscriptionForUDN.equals(registeredDevice.getIdentity().getUdn())) {
198 log.fine("Removing outgoing subscription: " + outgoingSubscription.getKey());
199 it.remove();
200 if (!shuttingDown) {
201 registry.getConfiguration().getRegistryListenerExecutor().execute(
202 new Runnable() {
203 public void run() {
204 outgoingSubscription.getItem().end(CancelReason.DEVICE_WAS_REMOVED, null);
205 }
206 }
207 );
208 }
209 }
210 }
211
212
213 if (!shuttingDown) {
214 for (final RegistryListener listener : registry.getListeners()) {
215 registry.getConfiguration().getRegistryListenerExecutor().execute(
216 new Runnable() {
217 public void run() {
218 listener.remoteDeviceRemoved(registry, registeredDevice);
219 }
220 }
221 );
222 }
223 }
224
225
226 getDeviceItems().remove(new RegistryItem(registeredDevice.getIdentity().getUdn()));
227
228 return true;
229 }
230
231 return false;
232 }
233
234 void removeAll() {
235 removeAll(false);
236 }
237
238 void removeAll(boolean shuttingDown) {
239 RemoteDevice[] allDevices = get().toArray(new RemoteDevice[get().size()]);
240 for (RemoteDevice device : allDevices) {
241 remove(device, shuttingDown);
242 }
243 }
244
245
246
247 void start() {
248
249 }
250
251 void maintain() {
252
253 if (getDeviceItems().isEmpty()) return;
254
255
256 Map<UDN, RemoteDevice> expiredRemoteDevices = new HashMap<>();
257 for (RegistryItem<UDN, RemoteDevice> remoteItem : getDeviceItems()) {
258 if (log.isLoggable(Level.FINEST))
259 log.finest("Device '" + remoteItem.getItem() + "' expires in seconds: "
260 + remoteItem.getExpirationDetails().getSecondsUntilExpiration());
261 if (remoteItem.getExpirationDetails().hasExpired(false)) {
262 expiredRemoteDevices.put(remoteItem.getKey(), remoteItem.getItem());
263 }
264 }
265 for (RemoteDevice remoteDevice : expiredRemoteDevices.values()) {
266 if (log.isLoggable(Level.FINE))
267 log.fine("Removing expired: " + remoteDevice);
268 remove(remoteDevice);
269 }
270
271
272 Set<RemoteGENASubscription> expiredOutgoingSubscriptions = new HashSet<>();
273 for (RegistryItem<String, RemoteGENASubscription> item : getSubscriptionItems()) {
274 if (item.getExpirationDetails().hasExpired(true)) {
275 expiredOutgoingSubscriptions.add(item.getItem());
276 }
277 }
278 for (RemoteGENASubscription subscription : expiredOutgoingSubscriptions) {
279 if (log.isLoggable(Level.FINEST))
280 log.fine("Renewing outgoing subscription: " + subscription);
281 renewOutgoingSubscription(subscription);
282 }
283 }
284
285 public void resume() {
286 log.fine("Updating remote device expiration timestamps on resume");
287 List<RemoteDeviceIdentity> toUpdate = new ArrayList<>();
288 for (RegistryItem<UDN, RemoteDevice> remoteItem : getDeviceItems()) {
289 toUpdate.add(remoteItem.getItem().getIdentity());
290 }
291 for (RemoteDeviceIdentity identity : toUpdate) {
292 update(identity);
293 }
294 }
295
296 void shutdown() {
297 log.fine("Cancelling all outgoing subscriptions to remote devices during shutdown");
298 List<RemoteGENASubscription> remoteSubscriptions = new ArrayList<>();
299 for (RegistryItem<String, RemoteGENASubscription> item : getSubscriptionItems()) {
300 remoteSubscriptions.add(item.getItem());
301 }
302 for (RemoteGENASubscription remoteSubscription : remoteSubscriptions) {
303
304 registry.getProtocolFactory()
305 .createSendingUnsubscribe(remoteSubscription)
306 .run();
307 }
308
309 log.fine("Removing all remote devices from registry during shutdown");
310 removeAll(true);
311 }
312
313
314
315 protected void renewOutgoingSubscription(final RemoteGENASubscription subscription) {
316 registry.executeAsyncProtocol(
317 registry.getProtocolFactory().createSendingRenewal(subscription)
318 );
319 }
320 }