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.meta.StateVariable;
26  import org.fourthline.cling.model.types.Datatype;
27  import org.fourthline.cling.model.types.DeviceType;
28  import org.fourthline.cling.test.data.SampleData;
29  import org.testng.annotations.DataProvider;
30  import org.testng.annotations.Test;
31  
32  import static org.testng.Assert.assertEquals;
33  
34  /**
35   * String value converters
36   * <p>
37   * Consider the following service class with all state variables of
38   * <code>string</code> UPnP datatype - but with a much more specific
39   * Java type:
40   * </p>
41   * <a class="citation" href="javacode://example.localservice.MyServiceWithStringConvertibles" style="include: INC1"/>
42   * <p>
43   * The state variables are all of UPnP datatype <code>string</code> because
44   * Cling knows that the Java type of the annotated field is "string convertible".
45   * This is always the case for <code>java.net.URI</code> and <code>java.net.URL</code>.
46   * </p>
47   * <p>
48   * Any other Java type you'd like to use for automatic string conversion has to be named
49   * in the <code>@UpnpService</code> annotation on the class, like the
50   * <code>MyStringConvertible</code>. Note that these types have to
51   * have an appropriate <code>toString()</code> method and a single argument constructor
52   * that accepts a <code>java.lang.String</code> ("from string" conversion).
53   * </p>
54   * <p>
55   * The <code>List&lt;Integer></code> is the collection you'd use in your service
56   * implementation to group several numbers. Let's assume that for UPnP communication
57   * you need a comma-separated representation of the individual values in a string,
58   * as is required by many of the UPnP A/V specifications. First, tell Cling that
59   * the state variable really is a string datatype, it can't infer that
60   * from the field type. Then, if an action has this output argument, instead of
61   * manually creating the comma-separated string you pick the appropriate converter
62   * from the classes in <code>org.fourthline.cling.model.types.csv.*</code> and return
63   * it from your action method. These are actually <code>java.util.List</code>
64   * implementations, so you could use them <em>instead</em> of
65   * <code>java.util.List</code> if you don't care about the dependency. Any action
66   * input argument value can also be converted from a comma-separated string
67   * representation to a list automatically - all you have to do is use the
68   * CSV converter class as an input argument type.
69   * </p>
70   */
71  public class StringConvertibleTest {
72  
73          public LocalDevice createTestDevice(Class serviceClass) throws Exception {
74  
75          LocalServiceBinder binder = new AnnotationLocalServiceBinder();
76          LocalService svc = binder.read(serviceClass);
77          svc.setManager(new DefaultServiceManager(svc, serviceClass));
78  
79          return new LocalDevice(
80                  SampleData.createLocalDeviceIdentity(),
81                  new DeviceType("mydomain", "CustomDevice", 1),
82                  new DeviceDetails("A Custom Device"),
83                  svc
84          );
85      }
86  
87      @DataProvider(name = "devices")
88      public Object[][] getDevices() {
89  
90  
91          try {
92              return new LocalDevice[][]{
93                      {createTestDevice(MyServiceWithStringConvertibles.class)},
94              };
95          } catch (Exception ex) {
96              ex.printStackTrace(System.err);
97              // Damn testng swallows exceptions in provider/factory methods
98              throw new RuntimeException(ex);
99          }
100     }
101 
102     @Test(dataProvider = "devices")
103     public void validateBinding(LocalDevice device) {
104 
105         LocalService svc = device.getServices()[0];
106 
107         assertEquals(svc.getStateVariables().length, 4);
108         for (StateVariable stateVariable : svc.getStateVariables()) {
109             assertEquals(stateVariable.getTypeDetails().getDatatype().getBuiltin(), Datatype.Builtin.STRING);
110         }
111 
112         assertEquals(svc.getActions().length, 9); // Has 8 actions plus QueryStateVariableAction!
113 
114         assertEquals(svc.getAction("SetMyURL").getArguments().length, 1);
115         assertEquals(svc.getAction("SetMyURL").getArguments()[0].getName(), "In");
116         assertEquals(svc.getAction("SetMyURL").getArguments()[0].getDirection(), ActionArgument.Direction.IN);
117         assertEquals(svc.getAction("SetMyURL").getArguments()[0].getRelatedStateVariableName(), "MyURL");
118         // The others are all the same...
119 
120     }
121 
122     @Test(dataProvider =  "devices")
123     public void invokeActions(LocalDevice device) {
124         LocalService svc = device.getServices()[0];
125 
126         ActionInvocation setMyURL = new ActionInvocation(svc.getAction("SetMyURL"));
127         setMyURL.setInput("In", "http://foo/bar");
128         svc.getExecutor(setMyURL.getAction()).execute(setMyURL);
129         assertEquals(setMyURL.getFailure(), null);
130         assertEquals(setMyURL.getOutput().length, 0);
131 
132         ActionInvocation getMyURL = new ActionInvocation(svc.getAction("GetMyURL"));
133         svc.getExecutor(getMyURL.getAction()).execute(getMyURL);
134         assertEquals(getMyURL.getFailure(), null);
135         assertEquals(getMyURL.getOutput().length, 1);
136         assertEquals(getMyURL.getOutput()[0].toString(), "http://foo/bar");
137 
138         ActionInvocation setMyURI = new ActionInvocation(svc.getAction("SetMyURI"));
139         setMyURI.setInput("In", "http://foo/bar");
140         svc.getExecutor(setMyURI.getAction()).execute(setMyURI);
141         assertEquals(setMyURI.getFailure(), null);
142         assertEquals(setMyURI.getOutput().length, 0);
143 
144         ActionInvocation getMyURI = new ActionInvocation(svc.getAction("GetMyURI"));
145         svc.getExecutor(getMyURI.getAction()).execute(getMyURI);
146         assertEquals(getMyURI.getFailure(), null);
147         assertEquals(getMyURI.getOutput().length, 1);
148         assertEquals(getMyURI.getOutput()[0].toString(), "http://foo/bar");
149 
150         ActionInvocation setMyNumbers = new ActionInvocation(svc.getAction("SetMyNumbers"));
151         setMyNumbers.setInput("In", "1,2,3");
152         svc.getExecutor(setMyNumbers.getAction()).execute(setMyNumbers);
153         assertEquals(setMyNumbers.getFailure(), null);
154         assertEquals(setMyNumbers.getOutput().length, 0);
155 
156         ActionInvocation getMyNumbers = new ActionInvocation(svc.getAction("GetMyNumbers"));
157         svc.getExecutor(getMyNumbers.getAction()).execute(getMyNumbers);
158         assertEquals(getMyNumbers.getFailure(), null);
159         assertEquals(getMyNumbers.getOutput().length, 1);
160         assertEquals(getMyNumbers.getOutput()[0].toString(), "1,2,3");
161 
162         ActionInvocation setMyStringConvertible = new ActionInvocation(svc.getAction("SetMyStringConvertible"));
163         setMyStringConvertible.setInput("In", "foobar");
164         svc.getExecutor(setMyStringConvertible.getAction()).execute(setMyStringConvertible);
165         assertEquals(setMyStringConvertible.getFailure(), null);
166         assertEquals(setMyStringConvertible.getOutput().length, 0);
167 
168         ActionInvocation getMyStringConvertible = new ActionInvocation(svc.getAction("GetMyStringConvertible"));
169         svc.getExecutor(getMyStringConvertible.getAction()).execute(getMyStringConvertible);
170         assertEquals(getMyStringConvertible.getFailure(), null);
171         assertEquals(getMyStringConvertible.getOutput().length, 1);
172         assertEquals(getMyStringConvertible.getOutput()[0].toString(), "foobar");
173 
174     }
175 }