xref: /titanic_41/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java (revision 4b22b9337f359bfd063322244f5336cc7c6ffcfa)
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 ident	"%Z%%M%	%I%	%E% SMI"
46 
47  */
48 
49 
50 import java.awt.*;
51 import java.awt.event.*;
52 import java.util.*;
53 import java.text.*;
54 import javax.swing.*;
55 import javax.swing.event.*;
56 
57 import com.apple.dnssd.*;
58 
59 
60 class	BrowserApp implements ListSelectionListener, ResolveListener
61 {
62 	static BrowserApp	app;
63 	JFrame				frame;
64 	DomainListModel		domainList;
65 	BrowserListModel	servicesList, serviceList;
66 	JList				domainPane, servicesPane, servicePane;
67 	DNSSDService		servicesBrowser, serviceBrowser, domainBrowser;
68 	JLabel				hostLabel, portLabel;
69 
BrowserApp()70 	public		BrowserApp()
71 	{
72 		frame = new JFrame("DNS-SD Service Browser");
73 		frame.addWindowListener(new WindowAdapter() {
74 			public void windowClosing(WindowEvent e) {System.exit(0);}
75 		});
76 
77 		domainList = new DomainListModel();
78 		servicesList = new ServicesBrowserListModel();
79 		serviceList = new BrowserListModel();
80 
81 		try {
82 			domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList);
83 
84 			servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
85 			serviceBrowser = null;
86 		}
87 		catch ( Exception ex) { terminateWithException( ex); }
88 
89 		this.setupSubPanes( frame.getContentPane());
90 		frame.pack();
91 		frame.setVisible(true);
92 	}
93 
setupSubPanes( Container parent)94 	protected void	setupSubPanes( Container parent)
95 	{
96 		parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS));
97 
98 		JPanel browserRow = new JPanel();
99 		browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS));
100 		domainPane = new JList( domainList);
101 		domainPane.addListSelectionListener( this);
102 		JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
103 		browserRow.add( domainScroller);
104 		servicesPane = new JList( servicesList);
105 		servicesPane.addListSelectionListener( this);
106 		JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
107 		browserRow.add( servicesScroller);
108 		servicePane = new JList( serviceList);
109 		servicePane.addListSelectionListener( this);
110 		JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
111 		browserRow.add( serviceScroller);
112 
113 /*
114 		JPanel buttonRow = new JPanel();
115 		buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS));
116 		buttonRow.add( Box.createHorizontalGlue());
117 		JButton connectButton = new JButton( "Don't Connect");
118 		buttonRow.add( connectButton);
119 		buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
120 */
121 
122 		JPanel labelRow = new JPanel();
123 		labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS));
124 		labelRow.add( new JLabel( "  Host: "));
125 		hostLabel = new JLabel();
126 		labelRow.add( hostLabel);
127 		labelRow.add( Box.createRigidArea( new Dimension( 32, 0)));
128 		labelRow.add( new JLabel( "Port: "));
129 		portLabel = new JLabel();
130 		labelRow.add( portLabel);
131 		labelRow.add( Box.createHorizontalGlue());
132 
133 		parent.add( browserRow);
134 		parent.add( Box.createRigidArea( new Dimension( 0, 8)));
135 		parent.add( labelRow);
136 //		parent.add( buttonRow);
137 		parent.add( Box.createRigidArea( new Dimension( 0, 16)));
138 	}
139 
valueChanged( ListSelectionEvent e)140 	public void valueChanged( ListSelectionEvent e)
141 	{
142 		try {
143 			if ( e.getSource() == domainPane && !e.getValueIsAdjusting())
144 			{
145 				int		newSel = domainPane.getSelectedIndex();
146 				if ( -1 != newSel)
147 				{
148 					if ( serviceBrowser != null)
149 						serviceBrowser.stop();
150 					serviceList.removeAllElements();
151 					servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
152 				}
153 			}
154 			else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting())
155 			{
156 				int		newSel = servicesPane.getSelectedIndex();
157 				if ( serviceBrowser != null)
158 					serviceBrowser.stop();
159 				serviceList.removeAllElements();
160 				if ( -1 != newSel)
161 					serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList);
162 			}
163 			else if ( e.getSource() == servicePane && !e.getValueIsAdjusting())
164 			{
165 				int		newSel = servicePane.getSelectedIndex();
166 
167 				hostLabel.setText( "");
168 				portLabel.setText( "");
169 
170 				if ( -1 != newSel)
171 				{
172 					DNSSD.resolve( 0, serviceList.getNthInterface( newSel),
173 										serviceList.getNthServiceName( newSel),
174 										serviceList.getNthRegType( newSel),
175 										serviceList.getNthDomain( newSel),
176 										new SwingResolveListener( this));
177 				}
178 			}
179 		}
180 		catch ( Exception ex) { terminateWithException( ex); }
181 	}
182 
serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, String hostName, int port, TXTRecord txtRecord)183 	public void	serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
184 								String hostName, int port, TXTRecord txtRecord)
185 	{
186 		hostLabel.setText( hostName);
187 		portLabel.setText( String.valueOf( port));
188 	}
189 
operationFailed( DNSSDService service, int errorCode)190 	public void	operationFailed( DNSSDService service, int errorCode)
191 	{
192 		// handle failure here
193 	}
194 
terminateWithException( Exception e)195 	protected static void	terminateWithException( Exception e)
196 	{
197 		e.printStackTrace();
198 		System.exit( -1);
199 	}
200 
main(String s[])201     public static void main(String s[])
202     {
203 		app = new BrowserApp();
204     }
205 }
206 
207 
208 class	BrowserListModel extends DefaultListModel implements BrowseListener, Runnable
209 {
BrowserListModel()210 	public		BrowserListModel()
211 	{
212 		addCache = new Vector();
213 		removeCache = new Vector();
214 	}
215 
216 	/* The Browser invokes this callback when a service is discovered. */
serviceFound( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)217 	public void	serviceFound( DNSSDService browser, int flags, int ifIndex,
218 							String serviceName, String regType, String domain)
219 	{
220 		addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex));
221 		if ( ( flags & DNSSD.MORE_COMING) == 0)
222 			this.scheduleOnEventThread();
223 	}
224 
serviceLost( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)225 	public void	serviceLost( DNSSDService browser, int flags, int ifIndex,
226 							String serviceName, String regType, String domain)
227 	{
228 		removeCache.add( serviceName);
229 		if ( ( flags & DNSSD.MORE_COMING) == 0)
230 			this.scheduleOnEventThread();
231 	}
232 
run()233 	public void	run()
234 	{
235 		while ( removeCache.size() > 0)
236 		{
237 			String	serviceName = (String) removeCache.remove( removeCache.size() - 1);
238 			int		matchInd = this.findMatching( serviceName);	// probably doesn't handle near-duplicates well.
239 			if ( matchInd != -1)
240 				this.removeElementAt( matchInd);
241 		}
242 		while ( addCache.size() > 0)
243 		{
244 			BrowserListElem	elem = (BrowserListElem) addCache.remove( addCache.size() - 1);
245 			if ( -1 == this.findMatching( elem.fServiceName))	// probably doesn't handle near-duplicates well.
246 				this.addInSortOrder( elem);
247 		}
248 	}
249 
operationFailed( DNSSDService service, int errorCode)250 	public void	operationFailed( DNSSDService service, int errorCode)
251 	{
252 		// handle failure here
253 	}
254 
255 	/* The list contains BrowserListElem's */
256 	class	BrowserListElem
257 	{
BrowserListElem( String serviceName, String domain, String type, int ifIndex)258 		public	BrowserListElem( String serviceName, String domain, String type, int ifIndex)
259 		{ fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
260 
toString()261 		public String	toString() { return fServiceName; }
262 
263 		public String	fServiceName, fDomain, fType;
264 		public int		fInt;
265 	}
266 
getNthServiceName( int n)267 	public String	getNthServiceName( int n)
268 	{
269 		BrowserListElem sel = (BrowserListElem) this.get( n);
270 		return sel.fServiceName;
271 	}
272 
getNthRegType( int n)273 	public String	getNthRegType( int n)
274 	{
275 		BrowserListElem sel = (BrowserListElem) this.get( n);
276 		return sel.fType;
277 	}
278 
getNthDomain( int n)279 	public String	getNthDomain( int n)
280 	{
281 		BrowserListElem sel = (BrowserListElem) this.get( n);
282 		return sel.fDomain;
283 	}
284 
getNthInterface( int n)285 	public int		getNthInterface( int n)
286 	{
287 		BrowserListElem sel = (BrowserListElem) this.get( n);
288 		return sel.fInt;
289 	}
290 
addInSortOrder( Object obj)291 	protected void	addInSortOrder( Object obj)
292 	{
293 		int	i;
294 		for ( i = 0; i < this.size(); i++)
295 			if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0)
296 				break;
297 		this.add( i, obj);
298 	}
299 
findMatching( String match)300 	protected int	findMatching( String match)
301 	{
302 		for ( int i = 0; i < this.size(); i++)
303 			if ( match.equals( this.getElementAt( i).toString()))
304 				return i;
305 		return -1;
306 	}
307 
scheduleOnEventThread()308 	protected void	scheduleOnEventThread()
309 	{
310 		try {
311 			SwingUtilities.invokeAndWait( this);
312 		}
313 		catch ( Exception e)
314 		{
315 			e.printStackTrace();
316 		}
317 	}
318 
319 	protected Vector	removeCache;	// list of serviceNames to remove
320 	protected Vector	addCache;		// list of BrowserListElem's to add
321 
322 	protected static Collator	sCollator;
323 
324 	static	// Initialize our static variables
325 	{
326 		sCollator = Collator.getInstance();
327 		sCollator.setStrength( Collator.PRIMARY);
328 	}
329 }
330 
331 
332 class	ServicesBrowserListModel extends BrowserListModel
333 {
334 	/* The Browser invokes this callback when a service is discovered. */
serviceFound( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)335 	public void	serviceFound( DNSSDService browser, int flags, int ifIndex,
336 							String serviceName, String regType, String domain)
337 	// Overridden to stuff serviceName into regType and make serviceName human-readable.
338 	{
339 		regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp.");
340 		super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
341 	}
342 
serviceLost( DNSSDService browser, int flags, int ifIndex, String serviceName, String regType, String domain)343 	public void	serviceLost( DNSSDService browser, int flags, int ifIndex,
344 							String serviceName, String regType, String domain)
345 	// Overridden to make serviceName human-readable.
346 	{
347 		super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
348 	}
349 
mapTypeToName( String type)350 	protected String	mapTypeToName( String type)
351 	// Convert a registration type into a human-readable string. Returns original string on no-match.
352 	{
353 		final String[]	namedServices = {
354 			"_afpovertcp",	"Apple File Sharing",
355 			"_http",		"World Wide Web servers",
356 			"_daap",		"Digital Audio Access",
357 			"_apple-sasl",	"Apple Password Servers",
358 			"_distcc",		"Distributed Compiler nodes",
359 			"_finger",		"Finger servers",
360 			"_ichat",		"iChat clients",
361 			"_presence",	"iChat AV clients",
362 			"_ssh",			"SSH servers",
363 			"_telnet",		"Telnet servers",
364 			"_workstation",	"Macintosh Manager clients",
365 			"_bootps",		"BootP servers",
366 			"_xserveraid",	"XServe RAID devices",
367 			"_eppc",		"Remote AppleEvents",
368 			"_ftp",			"FTP services",
369 			"_tftp",		"TFTP services"
370 		};
371 
372 		for ( int i = 0; i < namedServices.length; i+=2)
373 			if ( namedServices[i].equals( type))
374 				return namedServices[i + 1];
375 		return type;
376 	}
377 }
378 
379 
380 class	DomainListModel extends DefaultListModel implements DomainListener
381 {
382 	/* Called when a domain is discovered. */
domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)383 	public void	domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)
384 	{
385 		if ( !this.contains( domain))
386 			this.addElement( domain);
387 	}
388 
domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)389 	public void	domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)
390 	{
391 		if ( this.contains( domain))
392 			this.removeElement( domain);
393 	}
394 
operationFailed( DNSSDService service, int errorCode)395 	public void	operationFailed( DNSSDService service, int errorCode)
396 	{
397 		// handle failure here
398 	}
399 }
400 
401