1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.controlpoint;
17
18 import org.fourthline.cling.model.UnsupportedDataException;
19 import org.fourthline.cling.model.UserConstants;
20 import org.fourthline.cling.model.gena.CancelReason;
21 import org.fourthline.cling.model.gena.GENASubscription;
22 import org.fourthline.cling.model.gena.LocalGENASubscription;
23 import org.fourthline.cling.model.gena.RemoteGENASubscription;
24 import org.fourthline.cling.model.message.UpnpResponse;
25 import org.fourthline.cling.model.meta.LocalService;
26 import org.fourthline.cling.model.meta.RemoteService;
27 import org.fourthline.cling.model.meta.Service;
28 import org.fourthline.cling.protocol.ProtocolCreationException;
29 import org.fourthline.cling.protocol.sync.SendingSubscribe;
30 import org.seamless.util.Exceptions;
31
32 import java.util.Collections;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public abstract class SubscriptionCallback implements Runnable {
76
77 protected static Logger log = Logger.getLogger(SubscriptionCallback.class.getName());
78
79 protected final Service service;
80 protected final Integer requestedDurationSeconds;
81
82 private ControlPoint controlPoint;
83 private GENASubscription subscription;
84
85 protected SubscriptionCallback(Service service) {
86 this.service = service;
87 this.requestedDurationSeconds = UserConstants.DEFAULT_SUBSCRIPTION_DURATION_SECONDS;
88 }
89
90 protected SubscriptionCallback(Service service, int requestedDurationSeconds) {
91 this.service = service;
92 this.requestedDurationSeconds = requestedDurationSeconds;
93 }
94
95 public Service getService() {
96 return service;
97 }
98
99 synchronized public ControlPoint getControlPoint() {
100 return controlPoint;
101 }
102
103 synchronized public void setControlPoint(ControlPoint controlPoint) {
104 this.controlPoint = controlPoint;
105 }
106
107 synchronized public GENASubscription getSubscription() {
108 return subscription;
109 }
110
111 synchronized public void setSubscription(GENASubscription subscription) {
112 this.subscription = subscription;
113 }
114
115 synchronized public void run() {
116 if (getControlPoint() == null) {
117 throw new IllegalStateException("Callback must be executed through ControlPoint");
118 }
119
120 if (getService() instanceof LocalService) {
121 establishLocalSubscription((LocalService) service);
122 } else if (getService() instanceof RemoteService) {
123 establishRemoteSubscription((RemoteService) service);
124 }
125 }
126
127 private void establishLocalSubscription(LocalService service) {
128
129 if (getControlPoint().getRegistry().getLocalDevice(service.getDevice().getIdentity().getUdn(), false) == null) {
130 log.fine("Local device service is currently not registered, failing subscription immediately");
131 failed(null, null, new IllegalStateException("Local device is not registered"));
132 return;
133 }
134
135
136
137
138
139 LocalGENASubscription localSubscription = null;
140 try {
141 localSubscription =
142 new LocalGENASubscription(service, Integer.MAX_VALUE, Collections.EMPTY_LIST) {
143
144 public void failed(Exception ex) {
145 synchronized (SubscriptionCallback.this) {
146 SubscriptionCallback.this.setSubscription(null);
147 SubscriptionCallback.this.failed(null, null, ex);
148 }
149 }
150
151 public void established() {
152 synchronized (SubscriptionCallback.this) {
153 SubscriptionCallback.this.setSubscription(this);
154 SubscriptionCallback.this.established(this);
155 }
156 }
157
158 public void ended(CancelReason reason) {
159 synchronized (SubscriptionCallback.this) {
160 SubscriptionCallback.this.setSubscription(null);
161 SubscriptionCallback.this.ended(this, reason, null);
162 }
163 }
164
165 public void eventReceived() {
166 synchronized (SubscriptionCallback.this) {
167 log.fine("Local service state updated, notifying callback, sequence is: " + getCurrentSequence());
168 SubscriptionCallback.this.eventReceived(this);
169 incrementSequence();
170 }
171 }
172 };
173
174 log.fine("Local device service is currently registered, also registering subscription");
175 getControlPoint().getRegistry().addLocalSubscription(localSubscription);
176
177 log.fine("Notifying subscription callback of local subscription availablity");
178 localSubscription.establish();
179
180 log.fine("Simulating first initial event for local subscription callback, sequence: " + localSubscription.getCurrentSequence());
181 eventReceived(localSubscription);
182 localSubscription.incrementSequence();
183
184 log.fine("Starting to monitor state changes of local service");
185 localSubscription.registerOnService();
186
187 } catch (Exception ex) {
188 log.fine("Local callback creation failed: " + ex.toString());
189 log.log(Level.FINE, "Exception root cause: ", Exceptions.unwrap(ex));
190 if (localSubscription != null)
191 getControlPoint().getRegistry().removeLocalSubscription(localSubscription);
192 failed(localSubscription, null, ex);
193 }
194 }
195
196 private void establishRemoteSubscription(RemoteService service) {
197 RemoteGENASubscription remoteSubscription =
198 new RemoteGENASubscription(service, requestedDurationSeconds) {
199
200 public void failed(UpnpResponse responseStatus) {
201 synchronized (SubscriptionCallback.this) {
202 SubscriptionCallback.this.setSubscription(null);
203 SubscriptionCallback.this.failed(this, responseStatus, null);
204 }
205 }
206
207 public void established() {
208 synchronized (SubscriptionCallback.this) {
209 SubscriptionCallback.this.setSubscription(this);
210 SubscriptionCallback.this.established(this);
211 }
212 }
213
214 public void ended(CancelReason reason, UpnpResponse responseStatus) {
215 synchronized (SubscriptionCallback.this) {
216 SubscriptionCallback.this.setSubscription(null);
217 SubscriptionCallback.this.ended(this, reason, responseStatus);
218 }
219 }
220
221 public void eventReceived() {
222 synchronized (SubscriptionCallback.this) {
223 SubscriptionCallback.this.eventReceived(this);
224 }
225 }
226
227 public void eventsMissed(int numberOfMissedEvents) {
228 synchronized (SubscriptionCallback.this) {
229 SubscriptionCallback.this.eventsMissed(this, numberOfMissedEvents);
230 }
231 }
232
233 public void invalidMessage(UnsupportedDataException ex) {
234 synchronized (SubscriptionCallback.this) {
235 SubscriptionCallback.this.invalidMessage(this, ex);
236 }
237 }
238 };
239
240 SendingSubscribe protocol;
241 try {
242 protocol = getControlPoint().getProtocolFactory().createSendingSubscribe(remoteSubscription);
243 } catch (ProtocolCreationException ex) {
244 failed(subscription, null, ex);
245 return;
246 }
247 protocol.run();
248 }
249
250 synchronized public void end() {
251 if (subscription == null) return;
252 if (subscription instanceof LocalGENASubscription) {
253 endLocalSubscription((LocalGENASubscription)subscription);
254 } else if (subscription instanceof RemoteGENASubscription) {
255 endRemoteSubscription((RemoteGENASubscription)subscription);
256 }
257 }
258
259 private void endLocalSubscription(LocalGENASubscription subscription) {
260 log.fine("Removing local subscription and ending it in callback: " + subscription);
261 getControlPoint().getRegistry().removeLocalSubscription(subscription);
262 subscription.end(null);
263 }
264
265 private void endRemoteSubscription(RemoteGENASubscription subscription) {
266 log.fine("Ending remote subscription: " + subscription);
267 getControlPoint().getConfiguration().getSyncProtocolExecutorService().execute(
268 getControlPoint().getProtocolFactory().createSendingUnsubscribe(subscription)
269 );
270 }
271
272 protected void failed(GENASubscription subscription, UpnpResponse responseStatus, Exception exception) {
273 failed(subscription, responseStatus, exception, createDefaultFailureMessage(responseStatus, exception));
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287 protected abstract void failed(GENASubscription subscription, UpnpResponse responseStatus, Exception exception, String defaultMsg);
288
289
290
291
292
293
294 protected abstract void established(GENASubscription subscription);
295
296
297
298
299
300
301
302
303
304 protected abstract void ended(GENASubscription subscription, CancelReason reason, UpnpResponse responseStatus);
305
306
307
308
309
310
311
312
313
314
315 protected abstract void eventReceived(GENASubscription subscription);
316
317
318
319
320
321
322
323
324
325 protected abstract void eventsMissed(GENASubscription subscription, int numberOfMissedEvents);
326
327
328
329
330
331
332 public static String createDefaultFailureMessage(UpnpResponse responseStatus, Exception exception) {
333 String message = "Subscription failed: ";
334 if (responseStatus != null) {
335 message = message + " HTTP response was: " + responseStatus.getResponseDetails();
336 } else if (exception != null) {
337 message = message + " Exception occured: " + exception;
338 } else {
339 message = message + " No response received.";
340 }
341 return message;
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359 protected void invalidMessage(RemoteGENASubscription remoteGENASubscription,
360 UnsupportedDataException ex) {
361 log.info("Invalid event message received, causing: " + ex);
362 if (log.isLoggable(Level.FINE)) {
363 log.fine("------------------------------------------------------------------------------");
364 log.fine(ex.getData() != null ? ex.getData().toString() : "null");
365 log.fine("------------------------------------------------------------------------------");
366 }
367 }
368
369 @Override
370 public String toString() {
371 return "(SubscriptionCallback) " + getService();
372 }
373
374 }