View Javadoc
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  package example.localservice;
16  
17  import org.fourthline.cling.binding.LocalServiceBinder;
18  import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder;
19  import org.fourthline.cling.model.DefaultServiceManager;
20  import org.fourthline.cling.model.action.ActionInvocation;
21  import org.fourthline.cling.model.meta.ActionArgument;
22  import org.fourthline.cling.model.meta.DeviceDetails;
23  import org.fourthline.cling.model.meta.LocalDevice;
24  import org.fourthline.cling.model.meta.LocalService;
25  import org.fourthline.cling.model.types.Datatype;
26  import org.fourthline.cling.model.types.UDADeviceType;
27  import org.fourthline.cling.model.types.UDAServiceId;
28  import org.fourthline.cling.model.types.UDAServiceType;
29  import org.fourthline.cling.test.data.SampleData;
30  import org.testng.annotations.DataProvider;
31  import org.testng.annotations.Test;
32  
33  import static org.testng.Assert.assertEquals;
34  
35  /**
36   * Annotating a service implementation
37   * <p>
38   * The previously shown service class had a few annotations on the class itself, declaring
39   * the name and version of the service. Then annotations on fields were used to declare the
40   * state variables of the service and annotations on methods to declare callable actions.
41   * </p>
42   * <p>
43   * Your service implementation might not have fields that directly map to UPnP state variables.
44   * </p>
45   * <div class="section">
46   * <a class="citation" href="javadoc://example.localservice.SwitchPowerAnnotatedClass"/>
47   * </div>
48   * <p>
49   * Cling tries to provide smart defaults. For example, the previously shown service classes
50   * did not name the related state variable of action output arguments, as required by UPnP.
51   * Cling will automatically detect that the <code>getStatus()</code> method is a JavaBean
52   * getter method (its name starts with <code>get</code> or <code>is</code>) and use the
53   * JavaBean property name to find the related state variable. In this case that would be
54   * the JavaBean property <code>status</code> and Cling is also smart enough to know that
55   * you really want the uppercase UPnP state variable named <code>Status</code>.
56   * </p>
57   * <div class="section">
58   * <a class="citation" href="javadoc://example.localservice.SwitchPowerNamedStateVariable"/>
59   * </div>
60   * <p>
61   * For the next example, let's assume you have a class that was already written, not
62   * necessarily  as a service backend for UPnP but for some other purpose. You can't
63   * redesign and rewrite your class without interrupting all existing code. Cling offers
64   * some flexibility in the mapping of action methods, especially how the output of
65   * an action call is obtained.
66   * </p>
67   * <div class="section">
68   * <a class="citation" href="javadoc://example.localservice.SwitchPowerExtraGetter"/>
69   * </div>
70   * <p>
71   * Alternatively, and especially if an action has several output arguments, you
72   * can return multiple values wrapped in a JavaBean from your action method.
73   * </p>
74   * <div class="section">
75   * <a class="citation" href="javadoc://example.localservice.SwitchPowerBeanReturn"/>
76   * </div>
77   */
78  public class BasicBindingTest {
79  
80      public LocalDevice createTestDevice(Class serviceClass) throws Exception {
81  
82          LocalServiceBinder binder = new AnnotationLocalServiceBinder();
83          LocalService svc = binder.read(serviceClass);
84          svc.setManager(new DefaultServiceManager(svc, serviceClass));
85  
86          return new LocalDevice(
87                  SampleData.createLocalDeviceIdentity(),
88                  new UDADeviceType("BinaryLight", 1),
89                  new DeviceDetails("Example Binary Light"),
90                  svc
91          );
92      }
93  
94      @DataProvider(name = "devices")
95      public Object[][] getDevices() {
96  
97  
98          try {
99              return new LocalDevice[][]{
100                     {createTestDevice(SwitchPowerNamedStateVariable.class)},
101                     {createTestDevice(SwitchPowerAnnotatedClass.class)},
102                     {createTestDevice(SwitchPowerExtraGetter.class)},
103                     {createTestDevice(SwitchPowerBeanReturn.class)},
104             };
105         } catch (Exception ex) {
106             ex.printStackTrace(System.err);
107             // Damn testng swallows exceptions in provider/factory methods
108             throw new RuntimeException(ex);
109         }
110     }
111 
112     @Test(dataProvider = "devices")
113     public void validateBinding(LocalDevice device) {
114 
115         LocalService svc = device.getServices()[0];
116 
117 /*
118         System.out.println("############################################################################");
119         ServiceDescriptorBinder binder = new DefaultUpnpServiceConfiguration().getServiceDescriptorBinderUDA10();
120         try {
121             System.out.println(binder.generate(svc));
122         } catch (DescriptorBindingException e) {
123             throw new RuntimeException(e);
124         }
125         System.out.println("############################################################################");
126 
127 */
128         assertEquals(svc.getServiceId().toString(), "urn:" + UDAServiceId.DEFAULT_NAMESPACE + ":serviceId:SwitchPower");
129         assertEquals(svc.getServiceType().toString(), "urn:" + UDAServiceType.DEFAULT_NAMESPACE + ":service:SwitchPower:1");
130 
131         assertEquals(svc.getStateVariables().length, 2);
132         assertEquals(svc.getStateVariable("Target").getTypeDetails().getDatatype().getBuiltin(), Datatype.Builtin.BOOLEAN);
133         assertEquals(svc.getStateVariable("Target").getTypeDetails().getDefaultValue(), "0");
134         assertEquals(svc.getStateVariable("Target").getEventDetails().isSendEvents(), false);
135 
136         assertEquals(svc.getStateVariable("Status").getTypeDetails().getDatatype().getBuiltin(), Datatype.Builtin.BOOLEAN);
137         assertEquals(svc.getStateVariable("Status").getTypeDetails().getDefaultValue(), "0");
138         assertEquals(svc.getStateVariable("Status").getEventDetails().isSendEvents(), true);
139 
140         assertEquals(svc.getActions().length, 4); // Has 3 actions plus QueryStateVariableAction!
141 
142         assertEquals(svc.getAction("SetTarget").getName(), "SetTarget");
143         assertEquals(svc.getAction("SetTarget").getArguments().length, 1);
144         assertEquals(svc.getAction("SetTarget").getArguments()[0].getName(), "NewTargetValue");
145         assertEquals(svc.getAction("SetTarget").getArguments()[0].getDirection(), ActionArgument.Direction.IN);
146         assertEquals(svc.getAction("SetTarget").getArguments()[0].getRelatedStateVariableName(), "Target");
147 
148         assertEquals(svc.getAction("GetTarget").getName(), "GetTarget");
149         assertEquals(svc.getAction("GetTarget").getArguments().length, 1);
150         assertEquals(svc.getAction("GetTarget").getArguments()[0].getName(), "RetTargetValue");
151         assertEquals(svc.getAction("GetTarget").getArguments()[0].getDirection(), ActionArgument.Direction.OUT);
152         assertEquals(svc.getAction("GetTarget").getArguments()[0].getRelatedStateVariableName(), "Target");
153         assertEquals(svc.getAction("GetTarget").getArguments()[0].isReturnValue(), true);
154 
155         assertEquals(svc.getAction("GetStatus").getName(), "GetStatus");
156         assertEquals(svc.getAction("GetStatus").getArguments().length, 1);
157         assertEquals(svc.getAction("GetStatus").getArguments()[0].getName(), "ResultStatus");
158         assertEquals(svc.getAction("GetStatus").getArguments()[0].getDirection(), ActionArgument.Direction.OUT);
159         assertEquals(svc.getAction("GetStatus").getArguments()[0].getRelatedStateVariableName(), "Status");
160         assertEquals(svc.getAction("GetStatus").getArguments()[0].isReturnValue(), true);
161 
162     }
163 
164     @Test(dataProvider =  "devices")
165     public void invokeActions(LocalDevice device) {
166         // We mostly care about the binding without exceptions, but let's also test invocation
167         LocalService svc = device.getServices()[0];
168 
169         ActionInvocation setTargetInvocation = new ActionInvocation(svc.getAction("SetTarget"));
170         setTargetInvocation.setInput("NewTargetValue", true);
171         svc.getExecutor(setTargetInvocation.getAction()).execute(setTargetInvocation);
172         assertEquals(setTargetInvocation.getFailure(), null);
173         assertEquals(setTargetInvocation.getOutput().length, 0);
174 
175         ActionInvocation getStatusInvocation = new ActionInvocation(svc.getAction("GetStatus"));
176         svc.getExecutor(getStatusInvocation.getAction()).execute(getStatusInvocation);
177         assertEquals(getStatusInvocation.getFailure(), null);
178         assertEquals(getStatusInvocation.getOutput().length, 1);
179         assertEquals(getStatusInvocation.getOutput()[0].toString(), "1");
180 
181         setTargetInvocation = new ActionInvocation(svc.getAction("SetTarget"));
182         setTargetInvocation.setInput("NewTargetValue", false);
183         svc.getExecutor(setTargetInvocation.getAction()).execute(setTargetInvocation);
184         assertEquals(setTargetInvocation.getFailure(), null);
185         assertEquals(setTargetInvocation.getOutput().length, 0);
186 
187         ActionInvocation queryStateVariableInvocation = new ActionInvocation(svc.getAction("QueryStateVariable"));
188         queryStateVariableInvocation.setInput("varName", "Status");
189         svc.getExecutor(queryStateVariableInvocation.getAction()).execute(queryStateVariableInvocation);
190         assertEquals(queryStateVariableInvocation.getFailure(), null);
191         assertEquals(queryStateVariableInvocation.getOutput().length, 1);
192         assertEquals(queryStateVariableInvocation.getOutput()[0].toString(), "0");
193 
194     }
195 
196 }