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