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  
16  package example.localservice;
17  
18  import org.fourthline.cling.binding.LocalServiceBinder;
19  import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder;
20  import org.fourthline.cling.model.DefaultServiceManager;
21  import org.fourthline.cling.model.action.ActionInvocation;
22  import org.fourthline.cling.model.action.RemoteActionInvocation;
23  import org.fourthline.cling.model.message.Connection;
24  import org.fourthline.cling.model.message.UpnpHeaders;
25  import org.fourthline.cling.model.message.header.UpnpHeader;
26  import org.fourthline.cling.model.message.header.UserAgentHeader;
27  import org.fourthline.cling.model.meta.DeviceDetails;
28  import org.fourthline.cling.model.meta.LocalDevice;
29  import org.fourthline.cling.model.meta.LocalService;
30  import org.fourthline.cling.model.profile.RemoteClientInfo;
31  import org.fourthline.cling.model.types.UDADeviceType;
32  import org.fourthline.cling.test.data.SampleData;
33  import org.testng.annotations.DataProvider;
34  import org.testng.annotations.Test;
35  
36  import java.net.InetAddress;
37  import java.net.UnknownHostException;
38  
39  import static org.testng.Assert.assertEquals;
40  
41  /**
42   * Accessing remote client information
43   * <p>
44   * Theoretically, your service implementation should work with any client, as UPnP is
45   * supposed to provide a compatibility layer. In practice, this never works as no
46   * UPnP client and server is fully compatible with the specifications (except Cling, of
47   * course).
48   * </p>
49   * <p>
50   * If your action method has a last (or only parameter) of type <code>RemoteClientInfo</code>,
51   * Cling will provide details about the control point calling your service:
52   * </p>
53   * <a class="citation" href="javacode://example.localservice.SwitchPowerWithClientInfo" style="include:CLIENT_INFO"/>
54   * <p>
55   * The <code>RemoteClientInfo</code> argument will only be available when this action method
56   * is processing a remote client call, an <code>ActionInvocation</code> executed by the
57   * local UPnP stack on a local service does not have remote client information and the
58   * argument will be <code>null</code>.
59   * </p>
60   * <p>
61   * A client's remote and local address might be <code>null</code> if the Cling
62   * transport layer was not able to obtain the connection's address.
63   * </p>
64   * <p>
65   * You can set extra response headers on the <code>RemoteClientInfo</code>, which will be
66   * returned to the client with the response of your UPnP action. There is also a
67   * <code>setResponseUserAgent()</code> method for your convenience.
68   * </p>
69   */
70  public class RemoteClientInfoTest {
71  
72      public LocalDevice createTestDevice(Class serviceClass) throws Exception {
73  
74          LocalServiceBinder binder = new AnnotationLocalServiceBinder();
75          LocalService svc = binder.read(serviceClass);
76          svc.setManager(new DefaultServiceManager(svc, serviceClass));
77  
78          return new LocalDevice(
79              SampleData.createLocalDeviceIdentity(),
80              new UDADeviceType("BinaryLight", 1),
81              new DeviceDetails("Example Binary Light"),
82              svc
83          );
84      }
85  
86      @DataProvider(name = "devices")
87      public Object[][] getDevices() {
88  
89  
90          try {
91              return new LocalDevice[][]{
92                  {createTestDevice(SwitchPowerWithClientInfo.class)}
93              };
94          } catch (Exception ex) {
95              ex.printStackTrace(System.err);
96              // Damn TestNG swallows exceptions in provider/factory methods
97              throw new RuntimeException(ex);
98          }
99      }
100 
101 
102     @Test(dataProvider = "devices")
103     public void invokeActions(LocalDevice device) throws Exception {
104         LocalService svc = device.getServices()[0];
105 
106         UpnpHeaders requestHeaders = new UpnpHeaders();
107         requestHeaders.add(UpnpHeader.Type.USER_AGENT, new UserAgentHeader("foo/bar"));
108         requestHeaders.add("X-MY-HEADER", "foo");
109 
110         RemoteClientInfo clientInfo = new RemoteClientInfo(
111             new Connection() {
112                 @Override
113                 public boolean isOpen() {
114                     return true;
115                 }
116 
117                 @Override
118                 public InetAddress getRemoteAddress() {
119                     try {
120                         return InetAddress.getByName("10.0.0.1");
121                     } catch (UnknownHostException ex) {
122                         throw new RuntimeException(ex);
123                     }
124                 }
125 
126                 @Override
127                 public InetAddress getLocalAddress() {
128                     try {
129                         return InetAddress.getByName("10.0.0.2");
130                     } catch (UnknownHostException ex) {
131                         throw new RuntimeException(ex);
132                     }
133                 }
134             },
135             requestHeaders
136         );
137 
138         ActionInvocation setTargetInvocation = new RemoteActionInvocation(
139             svc.getAction("SetTarget"), clientInfo
140         );
141 
142         setTargetInvocation.setInput("NewTargetValue", true);
143         svc.getExecutor(setTargetInvocation.getAction()).execute(setTargetInvocation);
144         assertEquals(setTargetInvocation.getFailure(), null);
145         assertEquals(setTargetInvocation.getOutput().length, 0);
146 
147         assertEquals(clientInfo.getExtraResponseHeaders().getFirstHeader("X-MY-HEADER"), "foobar");
148     }
149 
150 }