On Wed, 30 Jul 2025 05:32:25 GMT, Jeremy Wood <d...@openjdk.org> wrote:

>> Hmm..We need to take a look at how to handle serialization aspect of 
>> propListener
>> 
>> but as per 
>> https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization
>> 
>> 
>>> You can serialize a lambda expression if its target type and its captured 
>>> arguments are serializable. However, like inner classes, the serialization 
>>> of lambda expressions is strongly discouraged.
>
>>> You can serialize a lambda expression if its target type and its captured 
>>> arguments are serializable
> 
> I think that means `propListener` is NOT going to serialize, because 
> `PropertyChangeListener` isn't `Serializable`. I'm unfamiliar with these 
> details; I found this link helpful: 
> https://www.baeldung.com/java-serialize-lambda
> 
> Alternatively: if we put `propListener` in BasicPopupMenuUI: then I think 
> serialization will be less of an issue. `JComponent#ui` is transient, so we 
> don't try to serialize it (and all of its other listeners). This way the 
> JPopupMenu will come out on the other side of deserialization using whatever 
> the L&F is in that setup. This feels good-ish to me.
> 
> (I'm assuming most L&F's extend BasicPopupMenuUI?)
> 
> This is all just an idea/suggestion; as long as the deserialized JPopupMenu 
> behaves like the original JPopupMenu I think that should be fine.

Yes, it seems deserialized JPopupMenu fails hiding the popup menu owing to 
`propListener` not getting serialized.

Tried with modified testcase of yours and fixed in latest PR


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.PropertyChangeListener;
import java.io.*;

public class JPopupMenuSerializationTest {

    public static class MyThread implements Runnable {
        static Container box;
        static Component invo;

        public MyThread(Container cont, Component comp) {
            this.invo = comp;
            this.box = cont;
        }

        public void run() {
            System.out.println("Starting 3 second countdown...");
            try{
                Thread.currentThread().sleep(3000);
            } catch (Exception e) {}
            System.out.println("Removing popup invoker from the container.");
            box.remove(invo);
            box.repaint();
        }
    }

    public static void main(String[] args) throws Exception {
        JPopupMenuSerializationTest test = new JPopupMenuSerializationTest();
        test.testSerialization();
    }

    public void testSerialization() throws Exception {
        JPopupMenu popupMenu = new JPopupMenu();
        byte[] data = serialize(popupMenu);
        JPopupMenu copy = deserialize(data);

        test(copy);
    }

    private void test(JPopupMenu jpm) {
        JFrame frame = new JFrame("My frame");
        final Container pane = frame.getContentPane();
        pane.setLayout(new BorderLayout());
        pane.add(new JTextField("", 20), BorderLayout.NORTH);
        JButton btn = new JButton("Exit");
        final Thread[] thr = new Thread[] { null };
        btn.setAction(new AbstractAction() {
            public void actionPerformed(ActionEvent ev) {
                System.exit(0);
            }
        });
        btn.setText("Exit");
        pane.add(btn, BorderLayout.SOUTH);
        final JLabel label = new JLabel("Click here to invoke popup");
        label.addMouseListener(new MouseAdapter() {
            public void mouseReleased(MouseEvent e) {
                jpm.add("One");
                jpm.add("Two");
                jpm.add("Three");
                jpm.show(label,
                        label.getLocationOnScreen().x,
                        label.getLocationOnScreen().y);
            }

            public void mousePressed(MouseEvent e) {
                Thread thr = new Thread(new MyThread(pane, label));
                thr.start();
            }
        });
        pane.add(label, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    private static byte[] serialize(JPopupMenu popupMenu) throws IOException {
        try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {
            try (ObjectOutputStream objOut = new ObjectOutputStream(byteOut)) {
                objOut.writeObject(popupMenu);
            }
            return byteOut.toByteArray();
        }
    }

    private static JPopupMenu deserialize(byte[] data) throws IOException, 
ClassNotFoundException {
        try (ByteArrayInputStream byteIn = new ByteArrayInputStream(data)) {
            try (ObjectInputStream objIn = new ObjectInputStream(byteIn)) {
                return (JPopupMenu) objIn.readObject();
            }
        }
    }
}

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/26407#discussion_r2242121247

Reply via email to