]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/Java/BrowserApp.java
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / Clients / Java / BrowserApp.java
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