/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package thaw.plugins.mDns; import javax.jmdns.ServiceInfo; import javax.swing.DefaultListModel; import java.awt.Dialog; import java.awt.GridLayout; import java.awt.BorderLayout; import javax.swing.JPanel; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.io.IOException; import java.net.Socket; import java.util.HashMap; import java.util.Iterator; import javax.swing.JButton; import thaw.core.Logger; import thaw.core.I18n; import thaw.core.ThawRunnable; import thaw.plugins.MDns; /** * This panel implements Zeroconf (called Bonjour/RendezVous by apple) discovery for Thaw * * WARNING: for it to work, you must have a running freenet node on the same network subnet, using the MDNSDiscovery panel * * @author Florent Daignière <nextgens@freenetproject.org> * * @see http://wiki.freenetproject.org/MDNSDiscoverypanel * * @see http://www.dns-sd.org/ServiceTypes.html * @see http://www.multicastdns.org/ * @see http://jmdns.sourceforge.net/ * * TODO: implement the "Manual" mode * TODO: maybe we should have a small progressbar shown in a new popup to introduce a "delay" at startup */ public class MDNSDiscoveryPanel extends JDialog implements ActionListener, ThawRunnable { public interface MDNSDiscoveryPanelCallback { /** * Called upon exit from MDNSDiscoveryPanelCallback * It runs on its own thread but still, don't abuse it :) */ public void onMDNSDiscoverPanelClosure(boolean hasBeenCancelled); } private static final long serialVersionUID = 1L; public static final String FCP_SERVICE_TYPE = "_fcp._tcp.local."; private boolean goon; private boolean cancelledByUser = false; private ServiceInfo selectedValue; private final MDNSDiscoveryPanelCallback cb; private final Dialog owner; private final HashMap displayedServiceInfos; private final JList list; private final DefaultListModel listModel; private final MDns mDns; private JButton okButton; private JButton cancelButton; public MDNSDiscoveryPanel(Dialog owner, MDns mDns, MDNSDiscoveryPanelCallback cb) { super(owner, "ZeroConf"); this.mDns = mDns; this.owner = owner; this.cb = cb; this.displayedServiceInfos = new HashMap(); // The UI list = new JList(); listModel = new DefaultListModel(); JLabel mainLabel = new JLabel(I18n.getMessage("thaw.plugin.MDNS.searchingNode")); JLabel titleListLabel = new JLabel(I18n.getMessage("thaw.plugin.MDNS.nodeList")); list.setModel(listModel); list.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); mainLabel.setIcon(thaw.gui.IconBox.mDns); JPanel listPanel = new JPanel(new BorderLayout()); listPanel.add(titleListLabel, BorderLayout.NORTH); listPanel.add(new JScrollPane(list), BorderLayout.CENTER); getContentPane().setLayout(new BorderLayout(5, 5)); getContentPane().add(mainLabel, BorderLayout.NORTH); getContentPane().add(listPanel, BorderLayout.CENTER); okButton = new JButton(I18n.getMessage("thaw.common.ok")); cancelButton = new JButton(I18n.getMessage("thaw.common.cancel")); okButton.addActionListener(this); cancelButton.addActionListener(this); JPanel buttonPanel = new JPanel(new GridLayout(1, 2)); buttonPanel.add(okButton); buttonPanel.add(cancelButton); getContentPane().add(buttonPanel, BorderLayout.SOUTH); super.setSize(300, 300); } /** * The user has selected something: notify the main loop and process the data. */ public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if ((source == okButton) || (source == cancelButton)){ goon = false; if(source == okButton) { if(list.getSelectedValue() == null) return; selectedValue = (ServiceInfo) displayedServiceInfos.get(list.getSelectedValue()); } else cancelledByUser = true; synchronized (this) { notifyAll(); } Logger.info(this, "User has selected : " + selectedValue); } } /** * The main loop : TheRealMeat(TM) * */ public void run() { super.setLocationRelativeTo(this.getParent()); this.setVisible(true); owner.setEnabled(false); Logger.notice(this, "Show the MDNSDiscoveryPanel"); Socket testSocket = null; boolean isConfigValid = false; goon = true; do { // Loop until a selection is done while(goon) { synchronized (mDns.getMDNSDiscovery().getFoundNodes()) { if(mDns.getMDNSDiscovery().getFoundNodes().size() > 0) { listModel.clear(); Iterator it = mDns.getMDNSDiscovery().getFoundNodes().iterator(); while(it.hasNext()) { ServiceInfo current = (ServiceInfo) it.next(); listModel.addElement(current.getName()); displayedServiceInfos.put(current.getName(), current); } list.repaint(); } } try { synchronized (this) { wait(Integer.MAX_VALUE); } } catch (InterruptedException e) {} } if(cancelledByUser) break; else if(selectedValue == null) continue; Logger.debug(this, "We got something from the UI : let's try to connect"); try { // We try to connect to the server // TODO: implement a proper test! testSocket = new Socket(selectedValue.getHostAddress(), selectedValue.getPort()); isConfigValid = testSocket.isConnected(); // Close the fcp socket we have openned, cleanup testSocket.close(); } catch (IOException e) { isConfigValid = false; } Logger.debug(this, "isConfigValid ="+isConfigValid); // Reload, just in case it failed... goon = true; list.removeSelectionInterval(0, mDns.getMDNSDiscovery().getFoundNodes().size()); } while(!isConfigValid); this.setVisible(false); owner.setEnabled(true); this.dispose(); if (!cancelledByUser) { Logger.debug(this, "We got something that looks valid from the UI : let's propagate changes to the config"); // Save the config. now that we know it's valid mDns.getConfig().setValue("nodeAddress", selectedValue.getHostAddress()); mDns.getConfig().setValue("nodePort", new Integer(selectedValue.getPort()).toString()); mDns.getConfig().setValue("sameComputer", String.valueOf(mDns.getMDNSDiscovery().isHasTheSameIPAddress(selectedValue))); Logger.info(this, "We are done : configuration has been saved sucessfully."); }else Logger.info(this, "The user has cancelled!"); cb.onMDNSDiscoverPanelClosure(cancelledByUser); Logger.notice(this, "We got back from the MDNSDiscoveryPanel callback"); } public void stop() { cancelledByUser = true; } }