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.controlpoint;
16  
17  import org.fourthline.cling.mock.MockUpnpService;
18  import org.fourthline.cling.model.message.UpnpMessage;
19  import org.fourthline.cling.model.message.header.DeviceTypeHeader;
20  import org.fourthline.cling.model.message.header.HostHeader;
21  import org.fourthline.cling.model.message.header.MANHeader;
22  import org.fourthline.cling.model.message.header.MXHeader;
23  import org.fourthline.cling.model.message.header.RootDeviceHeader;
24  import org.fourthline.cling.model.message.header.STAllHeader;
25  import org.fourthline.cling.model.message.header.ServiceTypeHeader;
26  import org.fourthline.cling.model.message.header.UDADeviceTypeHeader;
27  import org.fourthline.cling.model.message.header.UDAServiceTypeHeader;
28  import org.fourthline.cling.model.message.header.UDNHeader;
29  import org.fourthline.cling.model.message.header.UpnpHeader;
30  import org.fourthline.cling.model.types.DeviceType;
31  import org.fourthline.cling.model.types.NotificationSubtype;
32  import org.fourthline.cling.model.types.ServiceType;
33  import org.fourthline.cling.model.types.UDADeviceType;
34  import org.fourthline.cling.model.types.UDAServiceType;
35  import org.fourthline.cling.model.types.UDN;
36  import org.fourthline.cling.protocol.async.SendingSearch;
37  import org.testng.annotations.Test;
38  
39  import java.util.UUID;
40  
41  import static org.testng.Assert.*;
42  
43  /**
44   * Searching the network
45   * <p>
46   * When your control point joins the network it probably won't know any UPnP devices and services that
47   * might be available. To learn about the present devices it can broadcast - actually with UDP multicast
48   * datagrams - a search message which will be received by every device. Each receiver then inspects the
49   * search message and decides if it should reply directly (with notification UDP datagrams) to the
50   * sending control point.
51   * </p>
52   * <p>
53   * Search messages carry a <em>search type</em> header and receivers consider this header when they
54   * evaluate a potential response. The Cling <code>ControlPoint</code> API accepts a
55   * <code>UpnpHeader</code> argument when creating outgoing search messages.
56   * </p>
57   * <a class="citation" href="javadoc://this#searchAll" style="read-title: false;"/>
58   * <a class="citation" href="javadoc://this#searchUDN" style="read-title: false;"/>
59   * <a class="citation" href="javadoc://this#searchDeviceType" style="read-title: false;"/>
60   * <a class="citation" href="javadoc://this#searchServiceType" style="read-title: false;"/>
61   */
62  public class SearchExecuteTest {
63  
64      /**
65       * <p>
66       * Most of the time you'd like all devices to respond to your search, this is what the
67       * dedicated <code>STAllHeader</code> is used for:
68       * </p>
69       * <a class="citation" href="javacode://this" style="include: SEARCH"/>
70       * <p>
71       * Notification messages will be received by your control point and you can listen to
72       * the <code>Registry</code> and inspect the found devices and their services. (By the
73       * way, if you call <code>search()</code> without any argument, that's the same.)
74       * </p>
75       */
76      @Test
77      public void searchAll() throws Exception {
78          MockUpnpService upnpService = new MockUpnpService();
79  
80          upnpService.getControlPoint().search(       // DOC: SEARCH
81                  new STAllHeader()
82          );                                          // DOC: SEARCH
83  
84          assertMessages(upnpService, new STAllHeader());
85      }
86  
87      /**
88       * <p>
89       * On the other hand, when you already know the unique device name (UDN) of the device you
90       * are searching for - maybe because your control point remembered it while it was turned off - you
91       * can send a message which will trigger a response from only a particular device:
92       * </p>
93       * <a class="citation" href="javacode://this" style="include: SEARCH"/>
94       * <p>
95       * This is mostly useful to avoid network congestion when dozens of devices might <em>all</em>
96       * respond to a search request. Your <code>Registry</code> listener code however still has to
97       * inspect each newly found device, as registrations might occur independently from searches.
98       * </p>
99       */
100     @Test
101     public void searchUDN() throws Exception {
102         MockUpnpService upnpService = new MockUpnpService();
103 
104         UDN udn = new UDN(UUID.randomUUID());
105         upnpService.getControlPoint().search(       // DOC: SEARCH
106                 new UDNHeader(udn)
107         );                                          // DOC: SEARCH
108 
109         assertMessages(upnpService, new UDNHeader(udn));
110     }
111 
112     /**
113      * <p>
114      * You can also search by device or service type. This search request will trigger responses
115      * from all devices of type "<code>urn:schemas-upnp-org:device:BinaryLight:1</code>":
116      * </p>
117      * <a class="citation" href="javacode://this" style="include: SEARCH_UDA"/>
118      * <p>
119      * If the desired device type is of a custom namespace, use this variation:
120      * </p>
121      * <a class="citation" id="javacode_dt_search_custom" href="javacode://this" style="include: SEARCH_CUSTOM"/>
122      */
123     @Test
124     public void searchDeviceType() throws Exception {
125         MockUpnpService upnpService = new MockUpnpService();
126 
127         UDADeviceType udaType = new UDADeviceType("BinaryLight");       // DOC: SEARCH_UDA
128         upnpService.getControlPoint().search(
129                 new UDADeviceTypeHeader(udaType)
130         );                                                              // DOC: SEARCH_UDA
131 
132         assertMessages(upnpService, new UDADeviceTypeHeader(udaType));
133 
134         upnpService.getRouter().getOutgoingDatagramMessages().clear();
135 
136         DeviceType type = new DeviceType("org-mydomain", "MyDeviceType", 1);    // DOC: SEARCH_CUSTOM
137         upnpService.getControlPoint().search(
138                 new DeviceTypeHeader(type)
139         );                                                                      // DOC: SEARCH_CUSTOM
140 
141         assertMessages(upnpService, new DeviceTypeHeader(type));
142     }
143 
144     /**
145      * <p>
146      * Or, you can search for all devices which implement a particular service type:
147      * </p>
148      * <a class="citation" href="javacode://this" style="include: SEARCH_UDA"/>
149      * <a class="citation" id="javacode_st_search_custom" href="javacode://this" style="include: SEARCH_CUSTOM"/>
150      */
151     @Test
152     public void searchServiceType() throws Exception {
153         MockUpnpService upnpService = new MockUpnpService();
154 
155         UDAServiceType udaType = new UDAServiceType("SwitchPower");      // DOC: SEARCH_UDA
156         upnpService.getControlPoint().search(
157                 new UDAServiceTypeHeader(udaType)
158         );                                                               // DOC: SEARCH_UDA
159 
160         assertMessages(upnpService, new UDAServiceTypeHeader(udaType));
161 
162         upnpService.getRouter().getOutgoingDatagramMessages().clear();
163 
164         ServiceType type = new ServiceType("org-mydomain", "MyServiceType", 1);    // DOC: SEARCH_CUSTOM
165         upnpService.getControlPoint().search(
166                 new ServiceTypeHeader(type)
167         );                                                                        // DOC: SEARCH_CUSTOM
168 
169         assertMessages(upnpService, new ServiceTypeHeader(type));
170     }
171 
172 
173     @Test
174     public void searchRoot() throws Exception {
175         MockUpnpService upnpService = new MockUpnpService();
176         upnpService.getControlPoint().search(new RootDeviceHeader());
177         assertMessages(upnpService, new RootDeviceHeader());
178     }
179 
180 
181     @Test
182     public void searchDefaults() {
183         SendingSearch search = new SendingSearch(new MockUpnpService());
184         assertEquals(search.getSearchTarget().getString(), new STAllHeader().getString());
185     }
186 
187     @Test(expectedExceptions = java.lang.IllegalArgumentException.class)
188     public void searchInvalidST() {
189         SendingSearch search = new SendingSearch(new MockUpnpService(), new MXHeader());
190     }
191 
192     protected void assertMessages(MockUpnpService upnpService, UpnpHeader header) throws Exception {
193         assertEquals(upnpService.getRouter().getOutgoingDatagramMessages().size(), 5);
194         for (UpnpMessage msg : upnpService.getRouter().getOutgoingDatagramMessages()) {
195             assertSearchMessage(msg, header);
196         }
197     }
198 
199     protected void assertSearchMessage(UpnpMessage msg, UpnpHeader searchTarget) {
200         assertEquals(msg.getHeaders().getFirstHeader(UpnpHeader.Type.MAN).getString(), new MANHeader(NotificationSubtype.DISCOVER.getHeaderString()).getString());
201         assertEquals(msg.getHeaders().getFirstHeader(UpnpHeader.Type.MX).getString(), new MXHeader().getString());
202         assertEquals(msg.getHeaders().getFirstHeader(UpnpHeader.Type.ST).getString(), searchTarget.getString());
203         assertEquals(msg.getHeaders().getFirstHeader(UpnpHeader.Type.HOST).getString(), new HostHeader().getString());
204     }
205 }