summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/Clients/Java/SimpleChat.java
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/Clients/Java/SimpleChat.java')
-rw-r--r--mDNSResponder/Clients/Java/SimpleChat.java333
1 files changed, 333 insertions, 0 deletions
diff --git a/mDNSResponder/Clients/Java/SimpleChat.java b/mDNSResponder/Clients/Java/SimpleChat.java
new file mode 100644
index 00000000..a1fee5c0
--- /dev/null
+++ b/mDNSResponder/Clients/Java/SimpleChat.java
@@ -0,0 +1,333 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ * ("Apple") in consideration of your agreement to the following terms, and your
+ * use, installation, modification or redistribution of this Apple software
+ * constitutes acceptance of these terms. If you do not agree with these terms,
+ * please do not use, install, modify or redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and subject
+ * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+ * copyrights in this original Apple software (the "Apple Software"), to use,
+ * reproduce, modify and redistribute the Apple Software, with or without
+ * modifications, in source and/or binary forms; provided that if you redistribute
+ * the Apple Software in its entirety and without modifications, you must retain
+ * this notice and the following text and disclaimers in all such redistributions of
+ * the Apple Software. Neither the name, trademarks, service marks or logos of
+ * Apple Computer, Inc. may be used to endorse or promote products derived from the
+ * Apple Software without specific prior written permission from Apple. Except as
+ * expressly stated in this notice, no other rights or licenses, express or implied,
+ * are granted by Apple herein, including but not limited to any patent rights that
+ * may be infringed by your derivative works or by other works in which the Apple
+ * Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ * COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ SimpleChat is a simple peer-to-peer chat program that demonstrates
+ DNS-SD registration, browsing, resolving and record-querying.
+
+ To do:
+ - implement better coloring algorithm
+ */
+
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.net.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+import com.apple.dnssd.*;
+
+
+class SimpleChat implements ResolveListener, RegisterListener, QueryListener,
+ ActionListener, ItemListener, Runnable
+{
+ Document textDoc; // Holds all the chat text
+ JTextField inputField; // Holds a pending chat response
+ String ourName; // name used to identify this user in chat
+ DNSSDService browser; // object that actively browses for other chat clients
+ DNSSDService resolver; // object that resolves other chat clients
+ DNSSDRegistration registration; // object that maintains our connection advertisement
+ JComboBox targetPicker; // Indicates who we're talking to
+ TargetListModel targetList; // and its list model
+ JButton sendButton; // Will send text in inputField to target
+ InetAddress buddyAddr; // and address
+ int buddyPort; // and port
+ DatagramPacket dataPacket; // Inbound data packet
+ DatagramSocket outSocket; // Outbound data socket
+ SimpleAttributeSet textAttribs;
+
+ static final String kChatExampleRegType = "_p2pchat._udp";
+ static final String kWireCharSet = "ISO-8859-1";
+
+ public SimpleChat() throws Exception
+ {
+ JFrame frame = new JFrame("SimpleChat");
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {System.exit(0);}
+ });
+
+ ourName = System.getProperty( "user.name");
+ targetList = new TargetListModel();
+ textAttribs = new SimpleAttributeSet();
+ DatagramSocket inSocket = new DatagramSocket();
+ dataPacket = new DatagramPacket( new byte[ 4096], 4096);
+ outSocket = new DatagramSocket();
+
+ this.setupSubPanes( frame.getContentPane(), frame.getRootPane());
+ frame.pack();
+ frame.setVisible(true);
+ inputField.requestFocusInWindow();
+
+ browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList));
+
+ registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this);
+
+ new ListenerThread( this, inSocket, dataPacket).start();
+ }
+
+ protected void setupSubPanes( Container parent, JRootPane rootPane)
+ {
+ parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS));
+
+ JPanel textRow = new JPanel();
+ textRow.setLayout( new BoxLayout( textRow, BoxLayout.X_AXIS));
+ textRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ JEditorPane textPane = new JEditorPane( "text/html", "<BR>");
+ textPane.setPreferredSize( new Dimension( 400, 300));
+ textPane.setEditable( false);
+ JScrollPane textScroller = new JScrollPane( textPane);
+ textRow.add( textScroller);
+ textRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ textDoc = textPane.getDocument();
+
+ JPanel addressRow = new JPanel();
+ addressRow.setLayout( new BoxLayout( addressRow, BoxLayout.X_AXIS));
+ targetPicker = new JComboBox( targetList);
+ targetPicker.addItemListener( this);
+ addressRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ addressRow.add( new JLabel( "Talk to: "));
+ addressRow.add( targetPicker);
+ addressRow.add( Box.createHorizontalGlue());
+
+ JPanel buttonRow = new JPanel();
+ buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS));
+ buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ inputField = new JTextField();
+ // prevent inputField from hijacking <Enter> key
+ inputField.getKeymap().removeKeyStrokeBinding( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0));
+ buttonRow.add( inputField);
+ sendButton = new JButton( "Send");
+ buttonRow.add( Box.createRigidArea( new Dimension( 8, 0)));
+ buttonRow.add( sendButton);
+ buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ rootPane.setDefaultButton( sendButton);
+ sendButton.addActionListener( this);
+ sendButton.setEnabled( false);
+
+ parent.add( Box.createRigidArea( new Dimension( 0, 16)));
+ parent.add( textRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 8)));
+ parent.add( addressRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 8)));
+ parent.add( buttonRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 16)));
+ }
+
+ public void serviceRegistered( DNSSDRegistration registration, int flags,
+ String serviceName, String regType, String domain)
+ {
+ ourName = serviceName; // might have been renamed on collision
+ }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ System.out.println( "Service reported error " + String.valueOf( errorCode));
+ }
+
+ public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord)
+ {
+ buddyPort = port;
+ try {
+ // Start a record query to obtain IP address from hostname
+ DNSSD.queryRecord( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */,
+ new SwingQueryListener( this));
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ resolver.stop();
+ }
+
+ public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName,
+ int rrtype, int rrclass, byte[] rdata, int ttl)
+ {
+ try {
+ buddyAddr = InetAddress.getByAddress( rdata);
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ sendButton.setEnabled( true);
+ }
+
+ public void actionPerformed( ActionEvent e) // invoked when Send button is hit
+ {
+ try
+ {
+ String sendString = ourName + ": " + inputField.getText();
+ byte[] sendData = sendString.getBytes( kWireCharSet);
+ outSocket.send( new DatagramPacket( sendData, sendData.length, buddyAddr, buddyPort));
+ StyleConstants.setForeground( textAttribs, Color.black);
+ textDoc.insertString( textDoc.getLength(), inputField.getText() + "\n", textAttribs);
+ inputField.setText( "");
+ }
+ catch ( Exception exception) { terminateWithException( exception); }
+ }
+
+ public void itemStateChanged( ItemEvent e) // invoked when Target selection changes
+ {
+ sendButton.setEnabled( false);
+ if ( e.getStateChange() == ItemEvent.SELECTED)
+ {
+ try {
+ TargetListElem sel = (TargetListElem) targetList.getSelectedItem();
+ resolver = DNSSD.resolve( 0, sel.fInt, sel.fServiceName, sel.fType, sel.fDomain, this);
+ }
+ catch ( Exception exception) { terminateWithException( exception); }
+ }
+ }
+
+ public void run() // invoked on event thread when inbound packet arrives
+ {
+ try
+ {
+ String inMessage = new String( dataPacket.getData(), 0, dataPacket.getLength(), kWireCharSet);
+ StyleConstants.setForeground( textAttribs, this.getColorFor( dataPacket.getData(), dataPacket.getLength()));
+ textDoc.insertString( textDoc.getLength(), inMessage + "\n", textAttribs);
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ }
+
+ protected Color getColorFor( byte[] chars, int length)
+ // Produce a mapping from a string to a color, suitable for text display
+ {
+ int rgb = 0;
+ for ( int i=0; i < length && chars[i] != ':'; i++)
+ rgb = rgb ^ ( (int) chars[i] << (i%3+2) * 8);
+ return new Color( rgb & 0x007F7FFF); // mask off high bits so it is a dark color
+
+// for ( int i=0; i < length && chars[i] != ':'; i++)
+
+ }
+
+ protected static void terminateWithException( Exception e)
+ {
+ e.printStackTrace();
+ System.exit( -1);
+ }
+
+ public static void main(String s[])
+ {
+ try {
+ new SimpleChat();
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ }
+}
+
+
+
+class TargetListElem
+{
+ public TargetListElem( String serviceName, String domain, String type, int ifIndex)
+ { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
+
+ public String toString() { return fServiceName; }
+
+ public String fServiceName, fDomain, fType;
+ public int fInt;
+}
+
+class TargetListModel extends DefaultComboBoxModel implements BrowseListener
+{
+ /* The Browser invokes this callback when a service is discovered. */
+ public void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well.
+
+ if ( match == null)
+ this.addElement( new TargetListElem( serviceName, domain, regType, ifIndex));
+ }
+
+ /* The Browser invokes this callback when a service disappears. */
+ public void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well.
+
+ if ( match != null)
+ this.removeElement( match);
+ }
+
+ /* The Browser invokes this callback when a service disappears. */
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ System.out.println( "Service reported error " + String.valueOf( errorCode));
+ }
+
+ protected TargetListElem findMatching( String match)
+ {
+ for ( int i = 0; i < this.getSize(); i++)
+ if ( match.equals( this.getElementAt( i).toString()))
+ return (TargetListElem) this.getElementAt( i);
+ return null;
+ }
+
+}
+
+
+// A ListenerThread runs its owner when datagram packet p appears on socket s.
+class ListenerThread extends Thread
+{
+ public ListenerThread( Runnable owner, DatagramSocket s, DatagramPacket p)
+ { fOwner = owner; fSocket = s; fPacket = p; }
+
+ public void run()
+ {
+ while ( true )
+ {
+ try
+ {
+ fSocket.receive( fPacket);
+ SwingUtilities.invokeAndWait( fOwner); // process data on main thread
+ }
+ catch( Exception e)
+ {
+ break; // terminate thread
+ }
+ }
+ }
+
+ protected Runnable fOwner;
+ protected DatagramSocket fSocket;
+ protected DatagramPacket fPacket;
+}
+
+
+