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