xref: /titanic_52/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java (revision 5ffb0c9b03b5149ff4f5821a62be4a52408ada2a)
1 /* -*- Mode: Java; tab-width: 4 -*-
2  *
3  * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4  *
5  * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
6  * ("Apple") in consideration of your agreement to the following terms, and your
7  * use, installation, modification or redistribution of this Apple software
8  * constitutes acceptance of these terms.  If you do not agree with these terms,
9  * please do not use, install, modify or redistribute this Apple software.
10  *
11  * In consideration of your agreement to abide by the following terms, and subject
12  * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
13  * copyrights in this original Apple software (the "Apple Software"), to use,
14  * reproduce, modify and redistribute the Apple Software, with or without
15  * modifications, in source and/or binary forms; provided that if you redistribute
16  * the Apple Software in its entirety and without modifications, you must retain
17  * this notice and the following text and disclaimers in all such redistributions of
18  * the Apple Software.  Neither the name, trademarks, service marks or logos of
19  * Apple Computer, Inc. may be used to endorse or promote products derived from the
20  * Apple Software without specific prior written permission from Apple.  Except as
21  * expressly stated in this notice, no other rights or licenses, express or implied,
22  * are granted by Apple herein, including but not limited to any patent rights that
23  * may be infringed by your derivative works or by other works in which the Apple
24  * Software may be incorporated.
25  *
26  * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
27  * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
28  * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
30  * COMBINATION WITH YOUR PRODUCTS.
31  *
32  * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
34  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
36  * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
37  * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
38  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 
40 	BrowserApp demonstrates how to use DNS-SD to browse for and resolve services.
41 
42 	To do:
43 	- display resolved TXTRecord
44  */
45 
46 
47 import java.awt.*;
48 import java.awt.event.*;
49 import java.util.*;
50 import java.text.*;
51 import javax.swing.*;
52 import javax.swing.event.*;
53 
54 import com.apple.dnssd.*;
55 
56 
57 class	BrowserApp implements ListSelectionListener, ResolveListener, Runnable
58 {
59 	static BrowserApp	app;
60 	JFrame				frame;
61 	DomainListModel		domainList;
62 	BrowserListModel	servicesList, serviceList;
63 	JList				domainPane, servicesPane, servicePane;
64 	DNSSDService		servicesBrowser, serviceBrowser, domainBrowser;
65 	JLabel				hostLabel, portLabel;
66 	String				hostNameForUpdate;
67 	int					portForUpdate;
68 
69 	public		BrowserApp()
70 	{
71 		frame = new JFrame("DNS-SD Service Browser");
72 		frame.addWindowListener(new WindowAdapter() {
73 			public void windowClosing(WindowEvent e) {System.exit(0);}
74 		});
75 
76 		domainList = new DomainListModel();
77 		servicesList = new ServicesBrowserListModel();
78 		serviceList = new BrowserListModel();
79 
80 		try {
81 			domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList);
82 
83 			servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
84 			serviceBrowser = null;
85 		}
86 		catch ( Exception ex) { terminateWithException( ex); }
87 
88 		this.setupSubPanes( frame.getContentPane());
89 		frame.pack();
90 		frame.setVisible(true);
91 	}
92 
93 	protected void	setupSubPanes( Container parent)
94 	{
95 		parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS));
96 
97 		JPanel browserRow = new JPanel();
98 		browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS));
99 		domainPane = new JList( domainList);
100 		domainPane.addListSelectionListener( this);
101 		JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
102 		browserRow.add( domainScroller);
103 		servicesPane = new JList( servicesList);
104 		servicesPane.addListSelectionListener( this);
105 		JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
106 		browserRow.add( servicesScroller);
107 		servicePane = new JList( serviceList);
108 		servicePane.addListSelectionListener( this);
109 		JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
110 		browserRow.add( serviceScroller);
111 
112 /*
113 		JPanel buttonRow = new JPanel();
114 		buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS));
115 		buttonRow.add( Box.createHorizontalGlue());
116 		JButton connectButton = new JButton( "Don't Connect");
117 		buttonRow.add( connectButton);
118 		buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
119 */
120 
121 		JPanel labelRow = new JPanel();
122 		labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS));
123 		labelRow.add( new JLabel( "  Host: "));
124 		hostLabel = new JLabel();
125 		labelRow.add( hostLabel);
126 		labelRow.add( Box.createRigidArea( new Dimension( 32, 0)));
127 		labelRow.add( new JLabel( "Port: "));
128 		portLabel = new JLabel();
129 		labelRow.add( portLabel);
130 		labelRow.add( Box.createHorizontalGlue());
131 
132 		parent.add( browserRow);
133 		parent.add( Box.createRigidArea( new Dimension( 0, 8)));
134 		parent.add( labelRow);
135 //		parent.add( buttonRow);
136 		parent.add( Box.createRigidArea( new Dimension( 0, 16)));
137 	}
138 
139 	public void valueChanged( ListSelectionEvent e)
140 	{
141 		try {
142 			if ( e.getSource() == domainPane && !e.getValueIsAdjusting())
143 			{
144 				int		newSel = domainPane.getSelectedIndex();
145 				if ( -1 != newSel)
146 				{
147 					if ( serviceBrowser != null)
148 						serviceBrowser.stop();
149 					serviceList.removeAllElements();
150 					servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
151 				}
152 			}
153 			else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting())
154 			{
155 				int		newSel = servicesPane.getSelectedIndex();
156 				if ( serviceBrowser != null)
157 					serviceBrowser.stop();
158 				serviceList.removeAllElements();
159 				if ( -1 != newSel)
160 					serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList);
161 			}
162 			else if ( e.getSource() == servicePane && !e.getValueIsAdjusting())
163 			{
164 				int		newSel = servicePane.getSelectedIndex();
165 
166 				hostLabel.setText( "");
167 				portLabel.setText( "");
168 
169 				if ( -1 != newSel)
170 				{
171 					DNSSD.resolve( 0, serviceList.getNthInterface( newSel),
172 										serviceList.getNthServiceName( newSel),
173 										serviceList.getNthRegType( newSel),
174 										serviceList.getNthDomain( newSel),
175 										this);
176 				}
177 			}
178 		}
179 		catch ( Exception ex) { terminateWithException( ex); }
180 	}
181 
182 	public void run()
183 	{
184 		hostLabel.setText( hostNameForUpdate);
185 		portLabel.setText( String.valueOf( portForUpdate));
186 	}
187 
188 	public void	serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
189 								String hostName, int port, TXTRecord txtRecord)
190 	{
191 		// We want to update GUI on the AWT event dispatching thread, but we can't stop
192 		// the resolve from that thread, since stop() is synchronized with this callback.
193 		// So, we stop the resolve on this thread, then invokeAndWait on the AWT event thread.
194 
195 		resolver.stop();
196 
197 		hostNameForUpdate = hostName;
198 		portForUpdate = port;
199 
200 		try {
201 			SwingUtilities.invokeAndWait(this);
202 		}
203 		catch ( Exception e)
204 		{
205 			e.printStackTrace();
206 		}
207 	}
208 
209 	public void	operationFailed( DNSSDService service, int errorCode)
210 	{
211 		service.stop();
212 		// handle failure here
213 	}
214 
215 	protected static void	terminateWithException( Exception e)
216 	{
217 		e.printStackTrace();
218 		System.exit( -1);
219 	}
220 
221     public static void main(String s[])
222     {
223 		app = new BrowserApp();
224     }
225 }
226 
227 
228 class	BrowserListModel extends DefaultListModel implements BrowseListener, Runnable
229 {
230 	public		BrowserListModel()
231 	{
232 		addCache = new Vector();
233 		removeCache = new Vector();
234 	}
235 
236 	/* The Browser invokes this callback when a service is discovered. */
237 	public void	serviceFound( DNSSDService browser, int flags, int ifIndex,
238 							String serviceName, String regType, String domain)
239 	{
240 		addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex));
241 		if ( ( flags & DNSSD.MORE_COMING) == 0)
242 			this.scheduleOnEventThread();
243 	}
244 
245 	public void	serviceLost( DNSSDService browser, int flags, int ifIndex,
246 							String serviceName, String regType, String domain)
247 	{
248 		removeCache.add( serviceName);
249 		if ( ( flags & DNSSD.MORE_COMING) == 0)
250 			this.scheduleOnEventThread();
251 	}
252 
253 	public void	run()
254 	{
255 		while ( removeCache.size() > 0)
256 		{
257 			String	serviceName = (String) removeCache.remove( removeCache.size() - 1);
258 			int		matchInd = this.findMatching( serviceName);	// probably doesn't handle near-duplicates well.
259 			if ( matchInd != -1)
260 				this.removeElementAt( matchInd);
261 		}
262 		while ( addCache.size() > 0)
263 		{
264 			BrowserListElem	elem = (BrowserListElem) addCache.remove( addCache.size() - 1);
265 			if ( -1 == this.findMatching( elem.fServiceName))	// probably doesn't handle near-duplicates well.
266 				this.addInSortOrder( elem);
267 		}
268 	}
269 
270 	public void	operationFailed( DNSSDService service, int errorCode)
271 	{
272 		// handle failure here
273 	}
274 
275 	/* The list contains BrowserListElem's */
276 	class	BrowserListElem
277 	{
278 		public	BrowserListElem( String serviceName, String domain, String type, int ifIndex)
279 		{ fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
280 
281 		public String	toString() { return fServiceName; }
282 
283 		public String	fServiceName, fDomain, fType;
284 		public int		fInt;
285 	}
286 
287 	public String	getNthServiceName( int n)
288 	{
289 		BrowserListElem sel = (BrowserListElem) this.get( n);
290 		return sel.fServiceName;
291 	}
292 
293 	public String	getNthRegType( int n)
294 	{
295 		BrowserListElem sel = (BrowserListElem) this.get( n);
296 		return sel.fType;
297 	}
298 
299 	public String	getNthDomain( int n)
300 	{
301 		BrowserListElem sel = (BrowserListElem) this.get( n);
302 		return sel.fDomain;
303 	}
304 
305 	public int		getNthInterface( int n)
306 	{
307 		BrowserListElem sel = (BrowserListElem) this.get( n);
308 		return sel.fInt;
309 	}
310 
311 	protected void	addInSortOrder( Object obj)
312 	{
313 		int	i;
314 		for ( i = 0; i < this.size(); i++)
315 			if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0)
316 				break;
317 		this.add( i, obj);
318 	}
319 
320 	protected int	findMatching( String match)
321 	{
322 		for ( int i = 0; i < this.size(); i++)
323 			if ( match.equals( this.getElementAt( i).toString()))
324 				return i;
325 		return -1;
326 	}
327 
328 	protected void	scheduleOnEventThread()
329 	{
330 		try {
331 			SwingUtilities.invokeAndWait( this);
332 		}
333 		catch ( Exception e)
334 		{
335 			e.printStackTrace();
336 		}
337 	}
338 
339 	protected Vector	removeCache;	// list of serviceNames to remove
340 	protected Vector	addCache;		// list of BrowserListElem's to add
341 
342 	protected static Collator	sCollator;
343 
344 	static	// Initialize our static variables
345 	{
346 		sCollator = Collator.getInstance();
347 		sCollator.setStrength( Collator.PRIMARY);
348 	}
349 }
350 
351 
352 class	ServicesBrowserListModel extends BrowserListModel
353 {
354 	/* The Browser invokes this callback when a service is discovered. */
355 	public void	serviceFound( DNSSDService browser, int flags, int ifIndex,
356 							String serviceName, String regType, String domain)
357 	// Overridden to stuff serviceName into regType and make serviceName human-readable.
358 	{
359 		regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp.");
360 		super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
361 	}
362 
363 	public void	serviceLost( DNSSDService browser, int flags, int ifIndex,
364 							String serviceName, String regType, String domain)
365 	// Overridden to make serviceName human-readable.
366 	{
367 		super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
368 	}
369 
370 	protected String	mapTypeToName( String type)
371 	// Convert a registration type into a human-readable string. Returns original string on no-match.
372 	{
373 		final String[]	namedServices = {
374 			"_afpovertcp",	"Apple File Sharing",
375 			"_http",		"World Wide Web servers",
376 			"_daap",		"Digital Audio Access",
377 			"_apple-sasl",	"Apple Password Servers",
378 			"_distcc",		"Distributed Compiler nodes",
379 			"_finger",		"Finger servers",
380 			"_ichat",		"iChat clients",
381 			"_presence",	"iChat AV clients",
382 			"_ssh",			"SSH servers",
383 			"_telnet",		"Telnet servers",
384 			"_workstation",	"Macintosh Manager clients",
385 			"_bootps",		"BootP servers",
386 			"_xserveraid",	"XServe RAID devices",
387 			"_eppc",		"Remote AppleEvents",
388 			"_ftp",			"FTP services",
389 			"_tftp",		"TFTP services"
390 		};
391 
392 		for ( int i = 0; i < namedServices.length; i+=2)
393 			if ( namedServices[i].equals( type))
394 				return namedServices[i + 1];
395 		return type;
396 	}
397 }
398 
399 
400 class	DomainListModel extends DefaultListModel implements DomainListener
401 {
402 	/* Called when a domain is discovered. */
403 	public void	domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)
404 	{
405 		if ( !this.contains( domain))
406 			this.addElement( domain);
407 	}
408 
409 	public void	domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)
410 	{
411 		if ( this.contains( domain))
412 			this.removeElement( domain);
413 	}
414 
415 	public void	operationFailed( DNSSDService service, int errorCode)
416 	{
417 		// handle failure here
418 	}
419 }
420 
421