1 /* -*- Mode: Java; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
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.
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.
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.
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.
40 BrowserApp demonstrates how to use DNS-SD to browse for and resolve services.
43 - display resolved TXTRecord
48 import java
.awt
.event
.*;
52 import javax
.swing
.event
.*;
54 import com
.apple
.dnssd
.*;
57 class BrowserApp
implements ListSelectionListener
, ResolveListener
, Runnable
59 static BrowserApp app
;
61 DomainListModel domainList
;
62 BrowserListModel servicesList
, serviceList
;
63 JList domainPane
, servicesPane
, servicePane
;
64 DNSSDService servicesBrowser
, serviceBrowser
, domainBrowser
;
65 JLabel hostLabel
, portLabel
;
66 String hostNameForUpdate
;
71 frame
= new JFrame("DNS-SD Service Browser");
72 frame
.addWindowListener(new WindowAdapter() {
73 public void windowClosing(WindowEvent e
) {System
.exit(0);}
76 domainList
= new DomainListModel();
77 servicesList
= new ServicesBrowserListModel();
78 serviceList
= new BrowserListModel();
81 domainBrowser
= DNSSD
.enumerateDomains( DNSSD
.BROWSE_DOMAINS
, 0, domainList
);
83 servicesBrowser
= DNSSD
.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList
);
84 serviceBrowser
= null;
86 catch ( Exception ex
) { terminateWithException( ex
); }
88 this.setupSubPanes( frame
.getContentPane());
90 frame
.setVisible(true);
93 protected void setupSubPanes( Container parent
)
95 parent
.setLayout( new BoxLayout( parent
, BoxLayout
.Y_AXIS
));
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
);
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)));
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());
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)));
139 public void valueChanged( ListSelectionEvent e
)
142 if ( e
.getSource() == domainPane
&& !e
.getValueIsAdjusting())
144 int newSel
= domainPane
.getSelectedIndex();
147 if ( serviceBrowser
!= null)
148 serviceBrowser
.stop();
149 serviceList
.removeAllElements();
150 servicesBrowser
= DNSSD
.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList
);
153 else if ( e
.getSource() == servicesPane
&& !e
.getValueIsAdjusting())
155 int newSel
= servicesPane
.getSelectedIndex();
156 if ( serviceBrowser
!= null)
157 serviceBrowser
.stop();
158 serviceList
.removeAllElements();
160 serviceBrowser
= DNSSD
.browse( 0, 0, servicesList
.getNthRegType( newSel
), "", serviceList
);
162 else if ( e
.getSource() == servicePane
&& !e
.getValueIsAdjusting())
164 int newSel
= servicePane
.getSelectedIndex();
166 hostLabel
.setText( "");
167 portLabel
.setText( "");
171 DNSSD
.resolve( 0, serviceList
.getNthInterface( newSel
),
172 serviceList
.getNthServiceName( newSel
),
173 serviceList
.getNthRegType( newSel
),
174 serviceList
.getNthDomain( newSel
),
179 catch ( Exception ex
) { terminateWithException( ex
); }
184 hostLabel
.setText( hostNameForUpdate
);
185 portLabel
.setText( String
.valueOf( portForUpdate
));
188 public void serviceResolved( DNSSDService resolver
, int flags
, int ifIndex
, String fullName
,
189 String hostName
, int port
, TXTRecord txtRecord
)
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.
197 hostNameForUpdate
= hostName
;
198 portForUpdate
= port
;
201 SwingUtilities
.invokeAndWait(this);
209 public void operationFailed( DNSSDService service
, int errorCode
)
212 // handle failure here
215 protected static void terminateWithException( Exception e
)
221 public static void main(String s
[])
223 app
= new BrowserApp();
228 class BrowserListModel
extends DefaultListModel
implements BrowseListener
, Runnable
230 public BrowserListModel()
232 addCache
= new Vector();
233 removeCache
= new Vector();
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
)
240 addCache
.add( new BrowserListElem( serviceName
, domain
, regType
, ifIndex
));
241 if ( ( flags
& DNSSD
.MORE_COMING
) == 0)
242 this.scheduleOnEventThread();
245 public void serviceLost( DNSSDService browser
, int flags
, int ifIndex
,
246 String serviceName
, String regType
, String domain
)
248 removeCache
.add( serviceName
);
249 if ( ( flags
& DNSSD
.MORE_COMING
) == 0)
250 this.scheduleOnEventThread();
255 while ( removeCache
.size() > 0)
257 String serviceName
= (String
) removeCache
.remove( removeCache
.size() - 1);
258 int matchInd
= this.findMatching( serviceName
); // probably doesn't handle near-duplicates well.
260 this.removeElementAt( matchInd
);
262 while ( addCache
.size() > 0)
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
);
270 public void operationFailed( DNSSDService service
, int errorCode
)
272 // handle failure here
275 /* The list contains BrowserListElem's */
276 class BrowserListElem
278 public BrowserListElem( String serviceName
, String domain
, String type
, int ifIndex
)
279 { fServiceName
= serviceName
; fDomain
= domain
; fType
= type
; fInt
= ifIndex
; }
281 public String
toString() { return fServiceName
; }
283 public String fServiceName
, fDomain
, fType
;
287 public String
getNthServiceName( int n
)
289 BrowserListElem sel
= (BrowserListElem
) this.get( n
);
290 return sel
.fServiceName
;
293 public String
getNthRegType( int n
)
295 BrowserListElem sel
= (BrowserListElem
) this.get( n
);
299 public String
getNthDomain( int n
)
301 BrowserListElem sel
= (BrowserListElem
) this.get( n
);
305 public int getNthInterface( int n
)
307 BrowserListElem sel
= (BrowserListElem
) this.get( n
);
311 protected void addInSortOrder( Object obj
)
314 for ( i
= 0; i
< this.size(); i
++)
315 if ( sCollator
.compare( obj
.toString(), this.getElementAt( i
).toString()) < 0)
320 protected int findMatching( String match
)
322 for ( int i
= 0; i
< this.size(); i
++)
323 if ( match
.equals( this.getElementAt( i
).toString()))
328 protected void scheduleOnEventThread()
331 SwingUtilities
.invokeAndWait( this);
339 protected Vector removeCache
; // list of serviceNames to remove
340 protected Vector addCache
; // list of BrowserListElem's to add
342 protected static Collator sCollator
;
344 static // Initialize our static variables
346 sCollator
= Collator
.getInstance();
347 sCollator
.setStrength( Collator
.PRIMARY
);
352 class ServicesBrowserListModel
extends BrowserListModel
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.
359 regType
= serviceName
+ ( regType
.startsWith( "_udp.") ?
"._udp." : "._tcp.");
360 super.serviceFound( browser
, flags
, ifIndex
, this.mapTypeToName( serviceName
), regType
, 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.
367 super.serviceLost( browser
, flags
, ifIndex
, this.mapTypeToName( serviceName
), regType
, domain
);
370 protected String
mapTypeToName( String type
)
371 // Convert a registration type into a human-readable string. Returns original string on no-match.
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"
392 for ( int i
= 0; i
< namedServices
.length
; i
+=2)
393 if ( namedServices
[i
].equals( type
))
394 return namedServices
[i
+ 1];
400 class DomainListModel
extends DefaultListModel
implements DomainListener
402 /* Called when a domain is discovered. */
403 public void domainFound( DNSSDService domainEnum
, int flags
, int ifIndex
, String domain
)
405 if ( !this.contains( domain
))
406 this.addElement( domain
);
409 public void domainLost( DNSSDService domainEnum
, int flags
, int ifIndex
, String domain
)
411 if ( this.contains( domain
))
412 this.removeElement( domain
);
415 public void operationFailed( DNSSDService service
, int errorCode
)
417 // handle failure here