xref: /titanic_53/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 
BrowserApp()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 
setupSubPanes( Container parent)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 
valueChanged( ListSelectionEvent e)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 
run()182 	public void run()
183 	{
184 		hostLabel.setText( hostNameForUpdate);
185 		portLabel.setText( String.valueOf( portForUpdate));
186 	}
187 
serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, String hostName, int port, TXTRecord txtRecord)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 
operationFailed( DNSSDService service, int errorCode)209 	public void	operationFailed( DNSSDService service, int errorCode)
210 	{
211 		service.stop();
212 		// handle failure here
213 	}
214 
terminateWithException( Exception e)215 	protected static void	terminateWithException( Exception e)
216 	{
217 		e.printStackTrace();
218 		System.exit( -1);
219 	}
220 
main(String s[])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 {
BrowserListModel()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. */
serviceFound( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)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 
serviceLost( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)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 
run()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 
operationFailed( DNSSDService service, int errorCode)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 	{
BrowserListElem( String serviceName, String domain, String type, int ifIndex)278 		public	BrowserListElem( String serviceName, String domain, String type, int ifIndex)
279 		{ fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
280 
toString()281 		public String	toString() { return fServiceName; }
282 
283 		public String	fServiceName, fDomain, fType;
284 		public int		fInt;
285 	}
286 
getNthServiceName( int n)287 	public String	getNthServiceName( int n)
288 	{
289 		BrowserListElem sel = (BrowserListElem) this.get( n);
290 		return sel.fServiceName;
291 	}
292 
getNthRegType( int n)293 	public String	getNthRegType( int n)
294 	{
295 		BrowserListElem sel = (BrowserListElem) this.get( n);
296 		return sel.fType;
297 	}
298 
getNthDomain( int n)299 	public String	getNthDomain( int n)
300 	{
301 		BrowserListElem sel = (BrowserListElem) this.get( n);
302 		return sel.fDomain;
303 	}
304 
getNthInterface( int n)305 	public int		getNthInterface( int n)
306 	{
307 		BrowserListElem sel = (BrowserListElem) this.get( n);
308 		return sel.fInt;
309 	}
310 
addInSortOrder( Object obj)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 
findMatching( String match)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 
scheduleOnEventThread()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. */
serviceFound( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)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 
serviceLost( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)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 
mapTypeToName( String type)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. */
domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)403 	public void	domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)
404 	{
405 		if ( !this.contains( domain))
406 			this.addElement( domain);
407 	}
408 
domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)409 	public void	domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)
410 	{
411 		if ( this.contains( domain))
412 			this.removeElement( domain);
413 	}
414 
operationFailed( DNSSDService service, int errorCode)415 	public void	operationFailed( DNSSDService service, int errorCode)
416 	{
417 		// handle failure here
418 	}
419 }
420 
421