1 /*
2 * Copyright (C) 2013 4th Line GmbH, Switzerland
3 *
4 * The contents of this file are subject to the terms of either the GNU
5 * Lesser General Public License Version 2 or later ("LGPL") or the
6 * Common Development and Distribution License Version 1 or later
7 * ("CDDL") (collectively, the "License"). You may not use this file
8 * except in compliance with the License. See LICENSE.txt for more
9 * information.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 */
15
16 package org.fourthline.cling.support.lastchange;
17
18 import org.fourthline.cling.model.DefaultServiceManager;
19 import org.fourthline.cling.model.meta.LocalService;
20 import org.fourthline.cling.model.meta.StateVariable;
21 import org.fourthline.cling.model.state.StateVariableValue;
22 import org.fourthline.cling.model.types.UnsignedIntegerFourBytes;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26
27 /**
28 * Handles the "initial" event state for GENA subscriptions to services using LastChange.
29 * <p>
30 * When a GENA subscription is made on your AVTransport/RenderingControl service, you have to
31 * read the initial state of the service. Ususally Cling would do this for you and simply
32 * access all the state variables of your service behind the scenes. But the
33 * AVTransport/RenderingControl service doesn't use regular UPnP state variable eventing
34 * internally, they rely on the awful "LastChange" mechanism for their "logical" instances.
35 * </p>
36 * <p>
37 * Use this {@link org.fourthline.cling.model.ServiceManager} instead of the default one for
38 * these services.
39 * </p>
40 *
41 * @author Christian Bauer
42 */
43 public class LastChangeAwareServiceManager<T extends LastChangeDelegator> extends DefaultServiceManager<T> {
44
45 final protected LastChangeParser lastChangeParser;
46
47 public LastChangeAwareServiceManager(LocalService<T> localService,
48 LastChangeParser lastChangeParser) {
49 this(localService, null, lastChangeParser);
50 }
51
52 public LastChangeAwareServiceManager(LocalService<T> localService,
53 Class<T> serviceClass,
54 LastChangeParser lastChangeParser) {
55 super(localService, serviceClass);
56 this.lastChangeParser = lastChangeParser;
57 }
58
59 protected LastChangeParser getLastChangeParser() {
60 return lastChangeParser;
61 }
62
63 /**
64 * Call this method to propagate all accumulated "LastChange" values to GENA subscribers.
65 */
66 public void fireLastChange() {
67
68 // We need to obtain locks in the right order to avoid deadlocks:
69 // 1. The lock() of the DefaultServiceManager
70 // 2. The monitor/synchronized of the LastChange.fire() method
71
72 lock();
73 try {
74 getImplementation().getLastChange().fire(getPropertyChangeSupport());
75 } finally {
76 unlock();
77 }
78 }
79
80 @Override
81 protected Collection<StateVariableValue> readInitialEventedStateVariableValues() throws Exception {
82
83 // We don't use the service's internal LastChange but a fresh new one just for
84 // this initial event. Modifying the internal one would trigger event notification's
85 // to other subscribers!
86 LastChange lc = new LastChange(getLastChangeParser());
87
88 // Get the current "logical" instances of the service
89 UnsignedIntegerFourBytes[] ids = getImplementation().getCurrentInstanceIds();
90 if (ids.length > 0) {
91 for (UnsignedIntegerFourBytes instanceId : ids) {
92 // Iterate through all "logical" instances and ask them what their state is
93 getImplementation().appendCurrentState(lc, instanceId);
94 }
95 } else {
96 // Use the default "logical" instance with ID 0
97 getImplementation().appendCurrentState(lc, new UnsignedIntegerFourBytes(0));
98 }
99
100 // Sum it all up and return it in the initial event to the GENA subscriber
101 StateVariable variable = getService().getStateVariable("LastChange");
102 Collection<StateVariableValue> values = new ArrayList<>();
103 values.add(new StateVariableValue(variable, lc.toString()));
104 return values;
105 }
106
107 }