Jump to content



Photo

  • Please log in to reply
9 replies to this topic

#1 Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 10 July 2014 - 18:53

Hi all, I've been writing this Java program that transfers files between computers and auto-discovers peers using multicast.  It hangs when i try to send a file, but eventually starts working.  I posted over on Stack Overflow, but (in true Stack Overflow fashion) was given a link to another topic that didn't answer my question.  Here's the link (I think that's allowed?): 

 

https://stackoverflo...ing-swingworker

 

--

 

Thanks for the help!




#2 Lant

Lant

    Neowinian Senior

  • Joined: 13-April 06

Posted 10 July 2014 - 19:16

There are several avenues you could try and take here:

  1. Use Wireshark to get a packet capture and see if the hang is simply caused by blocking on file transfer
  2. Eliminate the GUI and create a console interface to see if the problem is in the GUI
  3. Run under a debugger and watch thread activity to see what is happening

I would guess that there is a synchronous piece of code that is holding up the updating of the GUI (perhaps executing on the GUI thread rather than on another thread). Its been a long time since I've dealt with Java so I can't comment on the code.



#3 OP Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 10 July 2014 - 19:35

There are several avenues you could try and take here:

  1. Use Wireshark to get a packet capture and see if the hang is simply caused by blocking on file transfer
  2. Eliminate the GUI and create a console interface to see if the problem is in the GUI
  3. Run under a debugger and watch thread activity to see what is happening

I would guess that there is a synchronous piece of code that is holding up the updating of the GUI (perhaps executing on the GUI thread rather than on another thread). Its been a long time since I've dealt with Java so I can't comment on the code.

I have run it through the debugger but it isn't shedding any light on the issue.  The connection isn't even being attempted, though; the print statement before the connect() call doesn't even execute until the program "un-hangs."



#4 Lant

Lant

    Neowinian Senior

  • Joined: 13-April 06

Posted 10 July 2014 - 20:29

Is there no way to visualise thread execution with your debugger? I think netbeans might have something like this. (I would be using this in Visual Studio)



#5 OP Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 11 July 2014 - 11:48

I can step through each statement using the debugger in Netbeans, yes.  I've been doing that and it rarely hangs while debugging...something tells me there's a race condition I'm missing.



#6 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 10
  • Joined: 26-October 05

Posted 11 July 2014 - 17:08

If you can't reproduce the issue in the debugger, add more logging until you can isolate exactly which line of code is hanging. Note sometimes logging also makes races disappear, then you're kinda screwed  :s



#7 OP Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 11 July 2014 - 17:22

If you can't reproduce the issue in the debugger, add more logging until you can isolate exactly which line of code is hanging. Note sometimes logging also makes races disappear, then you're kinda screwed  :s

 

I discovered the issue - accesses to my combo box were eating up all the execution time on the EDT.  I have another class which handles multicast, and it was editing the combo box based on which peers were detected. Works flawlessly now.  Thanks for the tips!



#8 Lant

Lant

    Neowinian Senior

  • Joined: 13-April 06

Posted 12 July 2014 - 00:00

Glad you got it sorted. It might be worth posting the modified code so if people run into a similar issue they can see how to fix it.



#9 OP Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 14 July 2014 - 02:43

Sure - I'll post the program tomorrow



#10 OP Timmah339

Timmah339

    Neowinian

  • Joined: 08-June 08

Posted 14 July 2014 - 15:50

Here's the updated code.  The combo box I'm passing to my Multicast class was being accessed constantly, blocking the EDT from updating the GUI.  I solved it by copying the contents of the box into an array list on every invocation of the constructor.  If anyone wants the full code, I'll gladly share it.  Just know that the interface was built using netbeans, so I'm not sure if it'll compile in another editor.

 

Transfer.java - GUI class

package transfer;


import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import static javax.swing.JFileChooser.FILES_AND_DIRECTORIES;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


/**
 *
 * @author Tim
 */
public class Transfer extends javax.swing.JFrame {


    private final FileDrop fileDrop;
    private Client client;
    private final Server server;
    private final ClientMulticast clientMulticast;
    private final ServerMulticast serverMulticast;
    
    /**
     * Creates new form TransferUI
     *
     * @throws java.io.IOException
     */
    public Transfer() throws IOException {
        initComponents();
        
        this.setTitle("Transfer");
        peerBox.setEditable(false);
        peerBox.removeAllItems();
      
        System.out.println(peerBox.getSelectedIndex());
        
        fileDrop = new FileDrop(backgroundPanel, (java.io.File[] files) -> {
            
            System.out.println(files[0].isDirectory());
            
            if (peerBox.getSelectedIndex() != -1) {


                sendLabel.setText("Hello");
                try {
                    client = new Client(sendLabel, files, (Peer) peerBox.getSelectedItem());
                    
                    //Client property change listener - listens for updates to progress bar
                    client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
                        
                        if (null != evt1.getPropertyName()) switch (evt1.getPropertyName()) {
                            case "progress":
                                sendProgressBar.setValue((Integer) evt1.getNewValue());
                                break;
                        }
                    });
                    
                    client.execute();
                    
                } catch (Exception ex) {
                    System.out.println("Unable to send! IOException in FileDrop call.");
                    ex.printStackTrace(System.out);
                }


            } else {
                sendLabel.setText("Host not found");
            }
        });


           
        sendProgressBar.setMaximum(100);
        sendProgressBar.setMinimum(0);
        receiveProgressBar.setMaximum(100);
        receiveProgressBar.setMinimum(0);


        
        server = new Server(receiveLabel);


        //Server property change listener - listens for updates to progress bar
        server.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
            if ("progress".equals(evt1.getPropertyName())) {
                receiveProgressBar.setValue((Integer) evt1.getNewValue());
            }
        });
        
        server.execute();
        
        serverMulticast = new ServerMulticast();
        serverMulticast.execute();
        clientMulticast = new ClientMulticast(peerBox);
        clientMulticast.execute();


    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {...}// </editor-fold>                        




    private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
        Transfer guiObject = this;
        
        SwingWorker openFile = new SwingWorker<Void, String>() {


            @Override
            protected Void doInBackground() throws Exception {
                
                openButton.setEnabled(false);
                
                fileChooser.setFileSelectionMode(FILES_AND_DIRECTORIES);


                int returnVal = fileChooser.showOpenDialog(guiObject);
                
                if (returnVal == JFileChooser.APPROVE_OPTION && peerBox.getSelectedIndex() != -1) {
                    
                    File[] fileArray = fileChooser.getSelectedFiles();                    
                    
                    client = new Client(sendLabel, fileArray, (Peer) peerBox.getSelectedItem());
                    
                    //Client property change listener - listens for updates to progress bar
                    client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
                        if ("progress".equals(evt1.getPropertyName())) {
                            sendProgressBar.setValue((Integer) evt1.getNewValue());
                        }
                    });
                    
                    client.execute();
                    
                    //block this swingworker until client worker is done sending
                    while(!client.isDone()) { }
                }


                openButton.setEnabled(true);
                
                return null;
            }
        };


        openFile.execute();
    }                                          
    


    /**
     * @param args the command line arguments
     * @throws java.lang.ClassNotFoundException
     * @throws java.lang.InstantiationException
     * @throws java.lang.IllegalAccessException
     * @throws javax.swing.UnsupportedLookAndFeelException
     */
    public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Transfer");
        
        UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(() -> {
            try {
                new Transfer().setVisible(true);
            } catch (IOException ex) {
                Logger.getLogger(Transfer.class.getName()).log(Level.SEVERE, null, ex);
            }
        });
    }


    // Variables declaration - do not modify                     
    private javax.swing.JPanel backgroundPanel;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.JButton openButton;
    private javax.swing.JComboBox<Peer> peerBox;
    private javax.swing.JLabel receiveHeaderLabel;
    private javax.swing.JLabel receiveLabel;
    private javax.swing.JPanel receivePanel;
    private javax.swing.JProgressBar receiveProgressBar;
    private javax.swing.JLabel sendHeaderLabel;
    private javax.swing.JLabel sendLabel;
    private javax.swing.JPanel sendPanel;
    private javax.swing.JProgressBar sendProgressBar;
    // End of variables declaration                   
}

ClientMulticast.java - Sends multicast packets and receives responses.  Accepts the combo box from the GUI class as a constructor argument.

package transfer;


import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;


/**
 *
 * @author Tim
 */
public class ClientMulticast extends SwingWorker<Void, Peer> {


    private boolean peerPreviouslyFound;
    private byte[] sendData, receiveData;
    private final DatagramSocket mSocket;
    private DatagramPacket receivePacket;
    private final JComboBox<Peer> peerBox;
    private Peer peer;
    private ArrayList<Peer> peerList;


    public ClientMulticast(JComboBox<Peer> peerBox) throws SocketException {


        peerList = new ArrayList<>();
        this.peerBox = peerBox;


        mSocket = new DatagramSocket();
        mSocket.setBroadcast(true);
        mSocket.setSoTimeout(300);


        sendData = "CLIENT_MSG".getBytes();
    }


    @Override
    protected Void doInBackground() throws IOException, InterruptedException, InvocationTargetException {
        while (true) {
            try {
                receiveData = new byte[1024];
                receivePacket = new DatagramPacket(receiveData, receiveData.length);


                peerPreviouslyFound = false;


                //send broadcast message
                mSocket.send(new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.255"), 8888));


                //receive response
                mSocket.receive(receivePacket);


                //don't have to worry about responses from local host because
                //server rejects multicast packets from local host
                peer = new Peer(receivePacket.getAddress(), receivePacket.getPort(), System.currentTimeMillis());


                for (Peer peerList1 : peerList) {
                    if (peerList1.getIPAddress().equals(peer.getIPAddress())) {
                        peerList1.setTimestamp(System.currentTimeMillis());
                        peerPreviouslyFound = true;
                        break;
                    }
                }


                //add to peer list only if reponse is valid, not from local host, and not previously received from this host
                if ("SERVER_RESPONSE".equalsIgnoreCase(new String(receivePacket.getData()).trim())
                        && !peerPreviouslyFound) {


                    //publish(peer);
                    peerBox.addItem(peer);
                    peerList.add(peer);
                }


                for (int i = 0; i < peerList.size(); i++) {
                    //if peer is greater than 5 seconds old, remove from list
                    if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
                        peerBox.removeItemAt(i);
                        peerList.remove(i);
                    }
                }
            } catch (SocketTimeoutException ex) {
                for (int i = 0; i < peerList.size(); i++) {
                    //if peer is greater than 5 seconds old, remove from list
                    if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
                        final int j = i;


                        SwingUtilities.invokeAndWait(() -> {
                            peerBox.removeItemAt(j);
                            peerList.remove(j);
                        });


                    }
                }
            }


            TimeUnit.MILLISECONDS.sleep(500);
        }//end while
    }


    @Override
    protected void process(List<Peer> p) {
        peerBox.addItem(p.get(p.size() - 1));
        peerList.add(p.get(p.size() - 1));
    }


}

Here's the updated code.  The combo box I'm passing to my Multicast class was being accessed constantly, blocking the EDT from updating the GUI.  I solved it by copying the contents of the box into an array list on every invocation of the constructor.  If anyone wants the full code, I'll gladly share it.  Just know that the interface was built using netbeans, so I'm not sure if it'll compile in another editor.

 

Transfer.java - GUI class

package transfer;


import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import static javax.swing.JFileChooser.FILES_AND_DIRECTORIES;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


/**
 *
 * @author Tim
 */
public class Transfer extends javax.swing.JFrame {


    private final FileDrop fileDrop;
    private Client client;
    private final Server server;
    private final ClientMulticast clientMulticast;
    private final ServerMulticast serverMulticast;
    
    /**
     * Creates new form TransferUI
     *
     * @throws java.io.IOException
     */
    public Transfer() throws IOException {
        initComponents();
        
        this.setTitle("Transfer");
        peerBox.setEditable(false);
        peerBox.removeAllItems();
      
        System.out.println(peerBox.getSelectedIndex());
        
        fileDrop = new FileDrop(backgroundPanel, (java.io.File[] files) -> {
            
            System.out.println(files[0].isDirectory());
            
            if (peerBox.getSelectedIndex() != -1) {


                sendLabel.setText("Hello");
                try {
                    client = new Client(sendLabel, files, (Peer) peerBox.getSelectedItem());
                    
                    //Client property change listener - listens for updates to progress bar
                    client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
                        
                        if (null != evt1.getPropertyName()) switch (evt1.getPropertyName()) {
                            case "progress":
                                sendProgressBar.setValue((Integer) evt1.getNewValue());
                                break;
                        }
                    });
                    
                    client.execute();
                    
                } catch (Exception ex) {
                    System.out.println("Unable to send! IOException in FileDrop call.");
                    ex.printStackTrace(System.out);
                }


            } else {
                sendLabel.setText("Host not found");
            }
        });


           
        sendProgressBar.setMaximum(100);
        sendProgressBar.setMinimum(0);
        receiveProgressBar.setMaximum(100);
        receiveProgressBar.setMinimum(0);


        
        server = new Server(receiveLabel);


        //Server property change listener - listens for updates to progress bar
        server.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
            if ("progress".equals(evt1.getPropertyName())) {
                receiveProgressBar.setValue((Integer) evt1.getNewValue());
            }
        });
        
        server.execute();
        
        serverMulticast = new ServerMulticast();
        serverMulticast.execute();
        clientMulticast = new ClientMulticast(peerBox);
        clientMulticast.execute();


    }
    
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {...}// </editor-fold>                        




    private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
        Transfer guiObject = this;
        
        SwingWorker openFile = new SwingWorker<Void, String>() {


            @Override
            protected Void doInBackground() throws Exception {
                
                openButton.setEnabled(false);
                
                fileChooser.setFileSelectionMode(FILES_AND_DIRECTORIES);


                int returnVal = fileChooser.showOpenDialog(guiObject);
                
                if (returnVal == JFileChooser.APPROVE_OPTION && peerBox.getSelectedIndex() != -1) {
                    
                    File[] fileArray = fileChooser.getSelectedFiles();                    
                    
                    client = new Client(sendLabel, fileArray, (Peer) peerBox.getSelectedItem());
                    
                    //Client property change listener - listens for updates to progress bar
                    client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
                        if ("progress".equals(evt1.getPropertyName())) {
                            sendProgressBar.setValue((Integer) evt1.getNewValue());
                        }
                    });
                    
                    client.execute();
                    
                    //block this swingworker until client worker is done sending
                    while(!client.isDone()) { }
                }


                openButton.setEnabled(true);
                
                return null;
            }
        };


        openFile.execute();
    }                                          
    


    /**
     * @param args the command line arguments
     * @throws java.lang.ClassNotFoundException
     * @throws java.lang.InstantiationException
     * @throws java.lang.IllegalAccessException
     * @throws javax.swing.UnsupportedLookAndFeelException
     */
    public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Transfer");
        
        UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(() -> {
            try {
                new Transfer().setVisible(true);
            } catch (IOException ex) {
                Logger.getLogger(Transfer.class.getName()).log(Level.SEVERE, null, ex);
            }
        });
    }


    // Variables declaration - do not modify                     
    private javax.swing.JPanel backgroundPanel;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.JButton openButton;
    private javax.swing.JComboBox<Peer> peerBox;
    private javax.swing.JLabel receiveHeaderLabel;
    private javax.swing.JLabel receiveLabel;
    private javax.swing.JPanel receivePanel;
    private javax.swing.JProgressBar receiveProgressBar;
    private javax.swing.JLabel sendHeaderLabel;
    private javax.swing.JLabel sendLabel;
    private javax.swing.JPanel sendPanel;
    private javax.swing.JProgressBar sendProgressBar;
    // End of variables declaration                   
}

ClientMulticast.java - Sends multicast packets and receives responses.  Accepts the combo box from the GUI class as a constructor argument.

package transfer;


import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;


/**
 *
 * @author Tim
 */
public class ClientMulticast extends SwingWorker<Void, Peer> {


    private boolean peerPreviouslyFound;
    private byte[] sendData, receiveData;
    private final DatagramSocket mSocket;
    private DatagramPacket receivePacket;
    private final JComboBox<Peer> peerBox;
    private Peer peer;
    private ArrayList<Peer> peerList;


    public ClientMulticast(JComboBox<Peer> peerBox) throws SocketException {


        peerList = new ArrayList<>();
        this.peerBox = peerBox;


        mSocket = new DatagramSocket();
        mSocket.setBroadcast(true);
        mSocket.setSoTimeout(300);


        sendData = "CLIENT_MSG".getBytes();
    }


    @Override
    protected Void doInBackground() throws IOException, InterruptedException, InvocationTargetException {
        while (true) {
            try {
                receiveData = new byte[1024];
                receivePacket = new DatagramPacket(receiveData, receiveData.length);


                peerPreviouslyFound = false;


                //send broadcast message
                mSocket.send(new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.255"), 8888));


                //receive response
                mSocket.receive(receivePacket);


                //don't have to worry about responses from local host because
                //server rejects multicast packets from local host
                peer = new Peer(receivePacket.getAddress(), receivePacket.getPort(), System.currentTimeMillis());


                for (Peer peerList1 : peerList) {
                    if (peerList1.getIPAddress().equals(peer.getIPAddress())) {
                        peerList1.setTimestamp(System.currentTimeMillis());
                        peerPreviouslyFound = true;
                        break;
                    }
                }


                //add to peer list only if reponse is valid, not from local host, and not previously received from this host
                if ("SERVER_RESPONSE".equalsIgnoreCase(new String(receivePacket.getData()).trim())
                        && !peerPreviouslyFound) {


                    //publish(peer);
                    peerBox.addItem(peer);
                    peerList.add(peer);
                }


                for (int i = 0; i < peerList.size(); i++) {
                    //if peer is greater than 5 seconds old, remove from list
                    if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
                        peerBox.removeItemAt(i);
                        peerList.remove(i);
                    }
                }
            } catch (SocketTimeoutException ex) {
                for (int i = 0; i < peerList.size(); i++) {
                    //if peer is greater than 5 seconds old, remove from list
                    if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
                        final int j = i;

                        //force GUI to update
                        SwingUtilities.invokeAndWait(() -> {
                            peerBox.removeItemAt(j);
                            peerList.remove(j);
                        });


                    }
                }
            }


            TimeUnit.MILLISECONDS.sleep(500);
        }//end while
    }


    @Override
    protected void process(List<Peer> p) {
        peerBox.addItem(p.get(p.size() - 1));
        peerList.add(p.get(p.size() - 1));
    }


}




Click here to login or here to register to remove this ad, it's free!