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