2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
5 * ("Apple") in consideration of your agreement to the following terms, and your
6 * use, installation, modification or redistribution of this Apple software
7 * constitutes acceptance of these terms. If you do not agree with these terms,
8 * please do not use, install, modify or redistribute this Apple software.
10 * In consideration of your agreement to abide by the following terms, and subject
11 * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
12 * copyrights in this original Apple software (the "Apple Software"), to use,
13 * reproduce, modify and redistribute the Apple Software, with or without
14 * modifications, in source and/or binary forms; provided that if you redistribute
15 * the Apple Software in its entirety and without modifications, you must retain
16 * this notice and the following text and disclaimers in all such redistributions of
17 * the Apple Software. Neither the name, trademarks, service marks or logos of
18 * Apple Computer, Inc. may be used to endorse or promote products derived from the
19 * Apple Software without specific prior written permission from Apple. Except as
20 * expressly stated in this notice, no other rights or licenses, express or implied,
21 * are granted by Apple herein, including but not limited to any patent rights that
22 * may be infringed by your derivative works or by other works in which the Apple
23 * Software may be incorporated.
25 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
26 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
27 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
29 * COMBINATION WITH YOUR PRODUCTS.
31 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
33 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
35 * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
36 * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
37 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 SimpleChat is a simple peer-to-peer chat program that demonstrates
40 DNS-SD registration, browsing, resolving and record-querying.
43 - implement better coloring algorithm
48 import java
.awt
.event
.*;
52 import javax
.swing
.event
.*;
53 import javax
.swing
.text
.*;
55 import com
.apple
.dnssd
.*;
58 class SimpleChat
implements ResolveListener
, RegisterListener
, QueryListener
,
59 ActionListener
, ItemListener
, Runnable
61 Document textDoc
; // Holds all the chat text
62 JTextField inputField
; // Holds a pending chat response
63 String ourName
; // name used to identify this user in chat
64 DNSSDService browser
; // object that actively browses for other chat clients
65 DNSSDService resolver
; // object that resolves other chat clients
66 DNSSDRegistration registration
; // object that maintains our connection advertisement
67 JComboBox targetPicker
; // Indicates who we're talking to
68 TargetListModel targetList
; // and its list model
69 JButton sendButton
; // Will send text in inputField to target
70 InetAddress buddyAddr
; // and address
71 int buddyPort
; // and port
72 DatagramPacket dataPacket
; // Inbound data packet
73 DatagramSocket outSocket
; // Outbound data socket
74 SimpleAttributeSet textAttribs
;
76 static final String kChatExampleRegType
= "_p2pchat._udp";
77 static final String kWireCharSet
= "ISO-8859-1";
79 public SimpleChat() throws Exception
81 JFrame frame
= new JFrame("SimpleChat");
82 frame
.addWindowListener(new WindowAdapter() {
83 public void windowClosing(WindowEvent e
) {System
.exit(0);}
86 ourName
= System
.getProperty( "user.name");
87 targetList
= new TargetListModel();
88 textAttribs
= new SimpleAttributeSet();
89 DatagramSocket inSocket
= new DatagramSocket();
90 dataPacket
= new DatagramPacket( new byte[ 4096], 4096);
91 outSocket
= new DatagramSocket();
93 this.setupSubPanes( frame
.getContentPane(), frame
.getRootPane());
95 frame
.setVisible(true);
96 inputField
.requestFocusInWindow();
98 browser
= DNSSD
.browse( 0, 0, kChatExampleRegType
, "", new SwingBrowseListener( targetList
));
100 registration
= DNSSD
.register( 0, 0, ourName
, kChatExampleRegType
, "", "", inSocket
.getLocalPort(), null, this);
102 new ListenerThread( this, inSocket
, dataPacket
).start();
105 protected void setupSubPanes( Container parent
, JRootPane rootPane
)
107 parent
.setLayout( new BoxLayout( parent
, BoxLayout
.Y_AXIS
));
109 JPanel textRow
= new JPanel();
110 textRow
.setLayout( new BoxLayout( textRow
, BoxLayout
.X_AXIS
));
111 textRow
.add( Box
.createRigidArea( new Dimension( 16, 0)));
112 JEditorPane textPane
= new JEditorPane( "text/html", "<BR>");
113 textPane
.setPreferredSize( new Dimension( 400, 300));
114 textPane
.setEditable( false);
115 JScrollPane textScroller
= new JScrollPane( textPane
);
116 textRow
.add( textScroller
);
117 textRow
.add( Box
.createRigidArea( new Dimension( 16, 0)));
118 textDoc
= textPane
.getDocument();
120 JPanel addressRow
= new JPanel();
121 addressRow
.setLayout( new BoxLayout( addressRow
, BoxLayout
.X_AXIS
));
122 targetPicker
= new JComboBox( targetList
);
123 targetPicker
.addItemListener( this);
124 addressRow
.add( Box
.createRigidArea( new Dimension( 16, 0)));
125 addressRow
.add( new JLabel( "Talk to: "));
126 addressRow
.add( targetPicker
);
127 addressRow
.add( Box
.createHorizontalGlue());
129 JPanel buttonRow
= new JPanel();
130 buttonRow
.setLayout( new BoxLayout( buttonRow
, BoxLayout
.X_AXIS
));
131 buttonRow
.add( Box
.createRigidArea( new Dimension( 16, 0)));
132 inputField
= new JTextField();
133 // prevent inputField from hijacking <Enter> key
134 inputField
.getKeymap().removeKeyStrokeBinding( KeyStroke
.getKeyStroke( KeyEvent
.VK_ENTER
, 0));
135 buttonRow
.add( inputField
);
136 sendButton
= new JButton( "Send");
137 buttonRow
.add( Box
.createRigidArea( new Dimension( 8, 0)));
138 buttonRow
.add( sendButton
);
139 buttonRow
.add( Box
.createRigidArea( new Dimension( 16, 0)));
140 rootPane
.setDefaultButton( sendButton
);
141 sendButton
.addActionListener( this);
142 sendButton
.setEnabled( false);
144 parent
.add( Box
.createRigidArea( new Dimension( 0, 16)));
145 parent
.add( textRow
);
146 parent
.add( Box
.createRigidArea( new Dimension( 0, 8)));
147 parent
.add( addressRow
);
148 parent
.add( Box
.createRigidArea( new Dimension( 0, 8)));
149 parent
.add( buttonRow
);
150 parent
.add( Box
.createRigidArea( new Dimension( 0, 16)));
153 public void serviceRegistered( DNSSDRegistration registration
, int flags
,
154 String serviceName
, String regType
, String domain
)
156 ourName
= serviceName
; // might have been renamed on collision
159 public void operationFailed( DNSSDService service
, int errorCode
)
161 System
.out
.println( "Service reported error " + String
.valueOf( errorCode
));
164 public void serviceResolved( DNSSDService resolver
, int flags
, int ifIndex
, String fullName
,
165 String hostName
, int port
, TXTRecord txtRecord
)
169 // Start a record query to obtain IP address from hostname
170 DNSSD
.queryRecord( 0, ifIndex
, hostName
, 1 /* ns_t_a */, 1 /* ns_c_in */,
171 new SwingQueryListener( this));
173 catch ( Exception e
) { terminateWithException( e
); }
177 public void queryAnswered( DNSSDService query
, int flags
, int ifIndex
, String fullName
,
178 int rrtype
, int rrclass
, byte[] rdata
, int ttl
)
181 buddyAddr
= InetAddress
.getByAddress( rdata
);
183 catch ( Exception e
) { terminateWithException( e
); }
184 sendButton
.setEnabled( true);
187 public void actionPerformed( ActionEvent e
) // invoked when Send button is hit
191 String sendString
= ourName
+ ": " + inputField
.getText();
192 byte[] sendData
= sendString
.getBytes( kWireCharSet
);
193 outSocket
.send( new DatagramPacket( sendData
, sendData
.length
, buddyAddr
, buddyPort
));
194 StyleConstants
.setForeground( textAttribs
, Color
.black
);
195 textDoc
.insertString( textDoc
.getLength(), inputField
.getText() + "\n", textAttribs
);
196 inputField
.setText( "");
198 catch ( Exception exception
) { terminateWithException( exception
); }
201 public void itemStateChanged( ItemEvent e
) // invoked when Target selection changes
203 sendButton
.setEnabled( false);
204 if ( e
.getStateChange() == ItemEvent
.SELECTED
)
207 TargetListElem sel
= (TargetListElem
) targetList
.getSelectedItem();
208 resolver
= DNSSD
.resolve( 0, sel
.fInt
, sel
.fServiceName
, sel
.fType
, sel
.fDomain
, this);
210 catch ( Exception exception
) { terminateWithException( exception
); }
214 public void run() // invoked on event thread when inbound packet arrives
218 String inMessage
= new String( dataPacket
.getData(), 0, dataPacket
.getLength(), kWireCharSet
);
219 StyleConstants
.setForeground( textAttribs
, this.getColorFor( dataPacket
.getData(), dataPacket
.getLength()));
220 textDoc
.insertString( textDoc
.getLength(), inMessage
+ "\n", textAttribs
);
222 catch ( Exception e
) { terminateWithException( e
); }
225 protected Color
getColorFor( byte[] chars
, int length
)
226 // Produce a mapping from a string to a color, suitable for text display
229 for ( int i
=0; i
< length
&& chars
[i
] != ':'; i
++)
230 rgb
= rgb ^
( (int) chars
[i
] << (i
%3+2) * 8);
231 return new Color( rgb
& 0x007F7FFF); // mask off high bits so it is a dark color
233 // for ( int i=0; i < length && chars[i] != ':'; i++)
237 protected static void terminateWithException( Exception e
)
243 public static void main(String s
[])
248 catch ( Exception e
) { terminateWithException( e
); }
256 public TargetListElem( String serviceName
, String domain
, String type
, int ifIndex
)
257 { fServiceName
= serviceName
; fDomain
= domain
; fType
= type
; fInt
= ifIndex
; }
259 public String
toString() { return fServiceName
; }
261 public String fServiceName
, fDomain
, fType
;
265 class TargetListModel
extends DefaultComboBoxModel
implements BrowseListener
267 /* The Browser invokes this callback when a service is discovered. */
268 public void serviceFound( DNSSDService browser
, int flags
, int ifIndex
,
269 String serviceName
, String regType
, String domain
)
271 TargetListElem match
= this.findMatching( serviceName
); // probably doesn't handle near-duplicates well.
274 this.addElement( new TargetListElem( serviceName
, domain
, regType
, ifIndex
));
277 /* The Browser invokes this callback when a service disappears. */
278 public void serviceLost( DNSSDService browser
, int flags
, int ifIndex
,
279 String serviceName
, String regType
, String domain
)
281 TargetListElem match
= this.findMatching( serviceName
); // probably doesn't handle near-duplicates well.
284 this.removeElement( match
);
287 /* The Browser invokes this callback when a service disappears. */
288 public void operationFailed( DNSSDService service
, int errorCode
)
290 System
.out
.println( "Service reported error " + String
.valueOf( errorCode
));
293 protected TargetListElem
findMatching( String match
)
295 for ( int i
= 0; i
< this.getSize(); i
++)
296 if ( match
.equals( this.getElementAt( i
).toString()))
297 return (TargetListElem
) this.getElementAt( i
);
304 // A ListenerThread runs its owner when datagram packet p appears on socket s.
305 class ListenerThread
extends Thread
307 public ListenerThread( Runnable owner
, DatagramSocket s
, DatagramPacket p
)
308 { fOwner
= owner
; fSocket
= s
; fPacket
= p
; }
316 fSocket
.receive( fPacket
);
317 SwingUtilities
.invokeAndWait( fOwner
); // process data on main thread
321 break; // terminate thread
326 protected Runnable fOwner
;
327 protected DatagramSocket fSocket
;
328 protected DatagramPacket fPacket
;