This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 206475
Collapse All | Expand All

(-)a/db/nbproject/project.xml (-1 / +1 lines)
Lines 90-96 Link Here
90
                    <build-prerequisite/>
90
                    <build-prerequisite/>
91
                    <compile-dependency/>
91
                    <compile-dependency/>
92
                    <run-dependency>
92
                    <run-dependency>
93
                        <specification-version>1.1</specification-version>
93
                        <specification-version>1.10</specification-version>
94
                    </run-dependency>
94
                    </run-dependency>
95
                </dependency>
95
                </dependency>
96
                <dependency>
96
                <dependency>
(-)a/db/src/org/netbeans/modules/db/explorer/Bundle.properties (-2 lines)
Lines 123-128 Link Here
123
DbExtendedDelete_ConfirmationTitle_Others={0,choice,1#Object|1<Objects}
123
DbExtendedDelete_ConfirmationTitle_Others={0,choice,1#Object|1<Objects}
124
124
125
DatabaseConnectionConvertor.password_description=A password for {0}
125
DatabaseConnectionConvertor.password_description=A password for {0}
126
DatabaseConnection_PleaseWaitTitle=Connection information
127
DatabaseConnection_PleaseWaitMessage=Please wait while reading connection information.
128
126
(-)a/db/src/org/netbeans/modules/db/explorer/DatabaseConnection.java (-127 / +19 lines)
Lines 47-54 Link Here
47
47
48
48
49
49
50
import java.awt.Dialog;
51
import java.awt.Dimension;
52
import java.beans.PropertyChangeListener;
50
import java.beans.PropertyChangeListener;
53
import java.beans.PropertyChangeSupport;
51
import java.beans.PropertyChangeSupport;
54
import java.beans.PropertyVetoException;
52
import java.beans.PropertyVetoException;
Lines 60-81 Link Here
60
import java.util.*;
58
import java.util.*;
61
import java.util.logging.Level;
59
import java.util.logging.Level;
62
import java.util.logging.Logger;
60
import java.util.logging.Logger;
63
import javax.swing.JComponent;
64
import javax.swing.JDialog;
65
import javax.swing.SwingUtilities;
66
import org.netbeans.api.db.explorer.DatabaseException;
61
import org.netbeans.api.db.explorer.DatabaseException;
67
import org.netbeans.api.db.explorer.JDBCDriver;
62
import org.netbeans.api.db.explorer.JDBCDriver;
68
import org.netbeans.api.db.explorer.JDBCDriverManager;
63
import org.netbeans.api.db.explorer.JDBCDriverManager;
69
import org.netbeans.api.keyring.Keyring;
64
import org.netbeans.api.keyring.Keyring;
70
import org.netbeans.api.progress.ProgressHandle;
71
import org.netbeans.api.progress.ProgressHandleFactory;
72
import org.netbeans.lib.ddl.CommandNotSupportedException;
65
import org.netbeans.lib.ddl.CommandNotSupportedException;
73
import org.netbeans.lib.ddl.DBConnection;
66
import org.netbeans.lib.ddl.DBConnection;
74
import org.netbeans.lib.ddl.DDLException;
67
import org.netbeans.lib.ddl.DDLException;
75
import org.netbeans.lib.ddl.impl.Specification;
68
import org.netbeans.lib.ddl.impl.Specification;
76
import org.netbeans.modules.db.ExceptionListener;
69
import org.netbeans.modules.db.ExceptionListener;
77
import org.netbeans.modules.db.explorer.action.ConnectAction;
70
import org.netbeans.modules.db.explorer.action.ConnectAction;
78
import org.netbeans.modules.db.explorer.dlg.ConnectProgressDialog;
79
import org.netbeans.modules.db.explorer.node.ConnectionNode;
71
import org.netbeans.modules.db.explorer.node.ConnectionNode;
80
import org.netbeans.modules.db.explorer.node.DDLHelper;
72
import org.netbeans.modules.db.explorer.node.DDLHelper;
81
import org.netbeans.modules.db.explorer.node.RootNode;
73
import org.netbeans.modules.db.explorer.node.RootNode;
Lines 92-98 Link Here
92
import org.openide.util.*;
84
import org.openide.util.*;
93
import org.openide.util.RequestProcessor.Task;
85
import org.openide.util.RequestProcessor.Task;
94
import org.openide.windows.TopComponent;
86
import org.openide.windows.TopComponent;
95
import org.openide.windows.WindowManager;
96
87
97
88
98
89
Lines 609-743 Link Here
609
            return ;
600
            return ;
610
        }
601
        }
611
        final String key = this.connectionFileName;
602
        final String key = this.connectionFileName;
612
        waitForReading(new Runnable() {
603
        // If the password was saved, then it means the user checked
613
            @Override
604
        // the box to say the password should be remembered.
614
            public void run() {
605
        char[] chars = Keyring.read(key);
615
                // If the password was saved, then it means the user checked
606
        if (chars != null) {
616
                // the box to say the password should be remembered.
607
            LOGGER.log(Level.FINE, "A password read for " + key);
617
                char[] chars = Keyring.read(key);
608
            pwd = String.valueOf(chars);
618
                if (chars != null) {
609
            rpwd = true;
619
                    LOGGER.log(Level.FINE, "A password read for " + key);
610
        } else {
620
                    pwd = String.valueOf(chars);
611
            LOGGER.log(Level.FINE, "No password read for " + key);
621
                    rpwd = true;
612
            pwd = "";
622
                } else {
613
            rpwd = false;
623
                    LOGGER.log(Level.FINE, "No password read for " + key);
614
        }
624
                    pwd = "";
625
                    rpwd = false;
626
                }
627
            }
628
            
629
        });
630
    }
615
    }
631
616
632
    public static void storePassword(final String key, final char[] pwd) {
617
    public static void storePassword(final String key, final char[] pwd) {
633
        Parameters.notNull("key", key);
618
        Parameters.notNull("key", key);
634
        Parameters.notNull("pwd", pwd);
619
        Parameters.notNull("pwd", pwd);
635
        RP.post(new Runnable() {
636
620
637
            @Override
621
        LOGGER.log(Level.FINE, "Storing password for " + key);
638
            public void run() {
622
        Keyring.save(key,
639
                LOGGER.log(Level.FINE, "Storing password for " + key);
623
                pwd,
640
                Keyring.save(key,
624
                NbBundle.getMessage(DatabaseConnectionConvertor.class,
641
                        pwd,
625
                    "DatabaseConnectionConvertor.password_description", key)); //NOI18N
642
                        NbBundle.getMessage(DatabaseConnectionConvertor.class,
643
                            "DatabaseConnectionConvertor.password_description", key)); //NOI18N
644
            }
645
        });
646
    }
647
    
648
    private void waitForReading(final Runnable toRun) {
649
        if (SwingUtilities.isEventDispatchThread()) {
650
            LOGGER.finest("Showing a wait dialog...");
651
            showWaitingDialog(toRun);
652
            LOGGER.finest("Showing a wait dialog - done.");
653
        } else {
654
            if (keyringTask != null && ! keyringTask.isFinished()) {
655
                LOGGER.finest("Wait for finished keyringTask");
656
                keyringTask.waitFinished();
657
                LOGGER.finest("keyringTask done.");
658
                toRun.run();
659
                return ;
660
            }            
661
            final Object lock = new Object();
662
            SwingUtilities.invokeLater(new Runnable() {
663
664
                @Override
665
                public void run() {
666
                    LOGGER.finest("Showing a wait dialog...");
667
                    showWaitingDialog(toRun);
668
                    synchronized (lock) {
669
                        lock.notifyAll();
670
                    }
671
                    LOGGER.finest("Showing a wait dialog - done.");
672
                }
673
            });
674
            try {
675
                synchronized (lock) {
676
                    lock.wait();
677
                }
678
            } catch (InterruptedException ex) {
679
                LOGGER.log(Level.INFO, ex.getMessage(), ex);
680
            }
681
        }
682
    }
683
    
684
    private RequestProcessor.Task keyringTask = null;
685
    
686
    private void showWaitingDialog(final Runnable toRun) {
687
        assert SwingUtilities.isEventDispatchThread();
688
689
        ProgressHandle progress = ProgressHandleFactory.createHandle("keyring");
690
        JComponent progressComponent = ProgressHandleFactory.createProgressComponent(progress);
691
        progress.start();
692
        ConnectProgressDialog panel = new ConnectProgressDialog(progressComponent,
693
                NbBundle.getMessage(DatabaseConnection.class, "DatabaseConnection_PleaseWaitMessage")); // NOI18N);
694
        panel.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage (ConnectAction.class, "ACS_ConnectingDialogTextA11yDesc")); // NOI18N
695
        final Dialog d = new JDialog(WindowManager.getDefault().getMainWindow(),
696
                                NbBundle.getMessage(DatabaseConnection.class, "DatabaseConnection_PleaseWaitTitle"), // NOI18N
697
                                true);
698
        d.add(panel);
699
        d.setSize(new Dimension(500, 100));
700
        d.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
701
        keyringTask = RP.post(new Runnable() {
702
703
            @Override
704
            public void run() {
705
                try {
706
                    toRun.run();
707
                } finally {
708
                    SwingUtilities.invokeLater(new Runnable() {
709
710
                        @Override
711
                        public void run() {
712
                            if (d != null) {
713
                                LOGGER.finest("Hide Waiting For Access to Keyring dialog.");
714
                                d.setVisible(false);
715
                                d.dispose();
716
                            }
717
                        }
718
                    });
719
                }
720
            }
721
        });
722
        LOGGER.finest("Show Waiting For Access to Keyring dialog.");
723
        try {
724
            Thread.sleep(100);
725
        } catch (InterruptedException ex) {
726
            LOGGER.log(Level.INFO, ex.getMessage(), ex);
727
        }
728
        d.setVisible(! keyringTask.isFinished());
729
    }
626
    }
730
    
627
    
731
    public static void deletePassword(final String key) {
628
    public static void deletePassword(final String key) {
732
        Parameters.notNull("key", key);
629
        Parameters.notNull("key", key);
733
        RP.post(new Runnable() {
734
630
735
            @Override
631
        LOGGER.log(Level.FINE, "Deleting password for " + key);
736
            public void run() {
632
        Keyring.delete(key);
737
                LOGGER.log(Level.FINE, "Deleting password for " + key);
738
                Keyring.delete(key);
739
            }
740
        });
741
    }
633
    }
742
    
634
    
743
    /** Returns if password should be remembered */
635
    /** Returns if password should be remembered */
(-)a/j2eeserver/nbproject/project.xml (-1 / +1 lines)
Lines 173-179 Link Here
173
                    <build-prerequisite/>
173
                    <build-prerequisite/>
174
                    <compile-dependency/>
174
                    <compile-dependency/>
175
                    <run-dependency>
175
                    <run-dependency>
176
                        <specification-version>1.5</specification-version>
176
                        <specification-version>1.10</specification-version>
177
                    </run-dependency>
177
                    </run-dependency>
178
                </dependency>
178
                </dependency>
179
                <dependency>
179
                <dependency>
(-)a/j2eeserver/src/org/netbeans/modules/j2ee/deployment/impl/Bundle.properties (-1 lines)
Lines 206-212 Link Here
206
206
207
MSG_KeyringDefaultDisplayName=Java EE server password
207
MSG_KeyringDefaultDisplayName=Java EE server password
208
MSG_KeyringDisplayName={0} passsword
208
MSG_KeyringDisplayName={0} passsword
209
MSG_KeyringAccess=Requesting keyring access
210
209
211
# Options export
210
# Options export
212
J2EE.Options.Export.displayName=Java EE
211
J2EE.Options.Export.displayName=Java EE
(-)a/j2eeserver/src/org/netbeans/modules/j2ee/deployment/impl/ServerRegistry.java (-94 / +27 lines)
Lines 46-53 Link Here
46
46
47
import java.io.File;
47
import java.io.File;
48
import java.io.IOException;
48
import java.io.IOException;
49
import java.util.concurrent.ExecutionException;
50
import java.util.concurrent.TimeoutException;
51
import java.util.logging.Logger;
49
import java.util.logging.Logger;
52
import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
50
import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
53
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
51
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
Lines 67-83 Link Here
67
import java.util.List;
65
import java.util.List;
68
import java.util.Map;
66
import java.util.Map;
69
import java.util.Set;
67
import java.util.Set;
70
import java.util.concurrent.Callable;
71
import java.util.concurrent.CopyOnWriteArrayList;
68
import java.util.concurrent.CopyOnWriteArrayList;
72
import java.util.concurrent.Future;
73
import java.util.concurrent.TimeUnit;
74
import java.util.logging.Level;
69
import java.util.logging.Level;
75
import javax.swing.SwingUtilities;
76
import org.netbeans.api.annotations.common.CheckForNull;
70
import org.netbeans.api.annotations.common.CheckForNull;
77
import org.netbeans.api.annotations.common.NonNull;
71
import org.netbeans.api.annotations.common.NonNull;
78
import org.netbeans.api.annotations.common.NullAllowed;
72
import org.netbeans.api.annotations.common.NullAllowed;
79
import org.netbeans.api.keyring.Keyring;
73
import org.netbeans.api.keyring.Keyring;
80
import org.netbeans.api.progress.ProgressUtils;
81
import org.netbeans.modules.j2ee.deployment.devmodules.spi.InstanceListener;
74
import org.netbeans.modules.j2ee.deployment.devmodules.spi.InstanceListener;
82
import org.netbeans.modules.j2ee.deployment.plugins.api.AlreadyRegisteredException;
75
import org.netbeans.modules.j2ee.deployment.plugins.api.AlreadyRegisteredException;
83
import org.netbeans.modules.j2ee.deployment.plugins.spi.OptionalDeploymentManagerFactory;
76
import org.netbeans.modules.j2ee.deployment.plugins.spi.OptionalDeploymentManagerFactory;
Lines 87-93 Link Here
87
import org.openide.filesystems.FileEvent;
80
import org.openide.filesystems.FileEvent;
88
import org.openide.filesystems.FileObject;
81
import org.openide.filesystems.FileObject;
89
import org.openide.filesystems.FileUtil;
82
import org.openide.filesystems.FileUtil;
90
import org.openide.util.RequestProcessor;
91
83
92
public final class ServerRegistry implements java.io.Serializable {
84
public final class ServerRegistry implements java.io.Serializable {
93
85
Lines 99-106 Link Here
99
    public static final String TARGETNAME_ATTR = "targetName"; //NOI18N
91
    public static final String TARGETNAME_ATTR = "targetName"; //NOI18N
100
    public static final String SERVER_NAME = "serverName"; //NOI18N
92
    public static final String SERVER_NAME = "serverName"; //NOI18N
101
    
93
    
102
    private static final RequestProcessor KEYRING_ACCESS = new RequestProcessor();
103
    
104
    private static ServerRegistry instance = null;
94
    private static ServerRegistry instance = null;
105
    public synchronized static ServerRegistry getInstance() {
95
    public synchronized static ServerRegistry getInstance() {
106
        if(instance == null) instance = new ServerRegistry();
96
        if(instance == null) instance = new ServerRegistry();
Lines 424-435 Link Here
424
                LOGGER.log(Level.INFO, null, ioe);
414
                LOGGER.log(Level.INFO, null, ioe);
425
            }
415
            }
426
        }
416
        }
427
            KEYRING_ACCESS.post(new Runnable() {
417
428
                @Override
418
        Keyring.delete(getPasswordKey(url));
429
                public void run() {
430
                    Keyring.delete(getPasswordKey(url));
431
                }
432
            });
433
    }
419
    }
434
420
435
    /**
421
    /**
Lines 651-747 Link Here
651
    public static Profiler getProfiler() {
637
    public static Profiler getProfiler() {
652
        return (Profiler)Lookup.getDefault().lookup(Profiler.class);
638
        return (Profiler)Lookup.getDefault().lookup(Profiler.class);
653
    }
639
    }
654
    
640
655
    @CheckForNull
641
    @CheckForNull
656
    static String readPassword(@NonNull final String url) {
642
    static String readPassword(@NonNull final String url) {
657
        Callable<String> call = new Callable<String>() {
643
        char[] passwordChars = Keyring.read(getPasswordKey(url));
658
            @Override
644
        if (passwordChars != null) {
659
            public String call() throws Exception {
645
            String password = String.valueOf(passwordChars);
660
                char[] passwordChars = Keyring.read(getPasswordKey(url));
646
            Arrays.fill(passwordChars, ' ');
661
                if (passwordChars != null) {
647
            return password;
662
                    String password = String.valueOf(passwordChars);
648
        }
663
                    Arrays.fill(passwordChars, ' ');
649
        return null;
664
                    return password;
665
                }
666
                return null;
667
            }
668
        };
669
        return readPassword(call);
670
    }
650
    }
671
    
651
672
    static void savePassword(@NonNull final String url, @NullAllowed final String password,
652
    static void savePassword(@NonNull final String url, @NullAllowed final String password,
673
            @NullAllowed final String displayName) {
653
            @NullAllowed final String displayName) {
674
        
654
675
        Runnable run = new Runnable() {
655
        if (password == null) {
676
            @Override
656
            return;
677
            public void run() {
657
        }
678
                if (password == null) {
658
        Keyring.save(getPasswordKey(url), password.toCharArray(), displayName);
679
                    return;
680
                }
681
                Keyring.save(getPasswordKey(url), password.toCharArray(), displayName);
682
            }
683
        };
684
        KEYRING_ACCESS.post(run);
685
    }
659
    }
686
    
660
    
687
    static void savePassword(@NonNull final FileObject fo, @NullAllowed final String password,
661
    static void savePassword(@NonNull final FileObject fo, @NullAllowed final String password,
688
            @NullAllowed final String displayName) {
662
            @NullAllowed final String displayName) {
689
        
663
        
690
        Runnable run = new Runnable() {
664
        if (password == null) {
691
665
            return;
692
            @Override
666
        }
693
            public void run() {
667
        String url = (String) fo.getAttribute(InstanceProperties.URL_ATTR);
694
                if (password == null) {
668
        if (url == null) {
695
                    return;
669
            return;
696
                }
670
        }
697
                String url = (String) fo.getAttribute(InstanceProperties.URL_ATTR);
671
        Keyring.save(getPasswordKey(url), password.toCharArray(), displayName);
698
                if (url == null) {
699
                    return;
700
                }
701
                Keyring.save(getPasswordKey(url), password.toCharArray(), displayName);
702
                try {
703
                    fo.setAttribute(InstanceProperties.PASSWORD_ATTR, null);
704
                } catch (IOException ex) {
705
                    LOGGER.log(Level.INFO, null, ex);
706
                }
707
            }
708
        };
709
        KEYRING_ACCESS.post(run);
710
    }    
711
    
712
    private static String readPassword(Callable<String> readTask) {
713
        try {
672
        try {
714
            final Future<String> result = KEYRING_ACCESS.submit(readTask);
673
            fo.setAttribute(InstanceProperties.PASSWORD_ATTR, null);
715
            if (SwingUtilities.isEventDispatchThread()) {
674
        } catch (IOException ex) {
716
                if (!result.isDone()) {
717
                    try {
718
                        // lets wait in awt to avoid flashing dialogs
719
                        result.get(50, TimeUnit.MILLISECONDS);
720
                    } catch (TimeoutException ex) {
721
                        ProgressUtils.showProgressDialogAndRun(new Runnable() {
722
723
                            @Override
724
                            public void run() {
725
                                try {
726
                                    result.get();
727
                                } catch (InterruptedException ex) {
728
                                    Thread.currentThread().interrupt();
729
                                } catch (ExecutionException ex) {
730
                                    LOGGER.log(Level.INFO, null, ex);
731
                                }
732
                            }
733
                        }, NbBundle.getMessage(ServerRegistry.class, "MSG_KeyringAccess"));
734
                    }
735
                }
736
            }
737
            return result.get();
738
        } catch (InterruptedException ex) {
739
            Thread.currentThread().interrupt();
740
        } catch (ExecutionException ex) {
741
            LOGGER.log(Level.INFO, null, ex);
675
            LOGGER.log(Level.INFO, null, ex);
742
        }
676
        }
743
        return null;         
677
    }    
744
    }
745
678
746
    private static String getPasswordKey(String url) {
679
    private static String getPasswordKey(String url) {
747
        StringBuilder builder = new StringBuilder("j2eeserver:");
680
        StringBuilder builder = new StringBuilder("j2eeserver:");
(-)a/j2eeserver/test/unit/src/org/netbeans/modules/j2ee/deployment/plugins/api/InstancePropertiesTest.java (-25 / +4 lines)
Lines 200-212 Link Here
200
200
201
    public void testPasswordInKeyring() throws Exception {
201
    public void testPasswordInKeyring() throws Exception {
202
        // the instance from test layer
202
        // the instance from test layer
203
        assertEquals("Adminpasswd", getPasswordFromKeyring("j2eeserver:fooservice"));
203
        assertEquals("Adminpasswd", new String(Keyring.read("j2eeserver:fooservice")));
204
204
205
        // new instance
205
        // new instance
206
        String url = TEST_URL_PREFIX + "passwordInKeyring";
206
        String url = TEST_URL_PREFIX + "passwordInKeyring";
207
        InstanceProperties.createInstanceProperties(
207
        InstanceProperties.createInstanceProperties(
208
                url, TEST_USERNAME, TEST_PASSWORD, TEST_DISPLAY_NAME);
208
                url, TEST_USERNAME, TEST_PASSWORD, TEST_DISPLAY_NAME);
209
        assertEquals(TEST_PASSWORD, getPasswordFromKeyring("j2eeserver:" + url));
209
        assertEquals(TEST_PASSWORD, new String(Keyring.read("j2eeserver:" + url)));
210
210
211
        // all password attributes are converted to keyring
211
        // all password attributes are converted to keyring
212
        FileObject fo = FileUtil.getConfigFile("J2EE/InstalledServers");
212
        FileObject fo = FileUtil.getConfigFile("J2EE/InstalledServers");
Lines 219-249 Link Here
219
        String url = TEST_URL_PREFIX + "keyringCleanup";
219
        String url = TEST_URL_PREFIX + "keyringCleanup";
220
        InstanceProperties.createInstanceProperties(
220
        InstanceProperties.createInstanceProperties(
221
                url, TEST_USERNAME, TEST_PASSWORD, TEST_DISPLAY_NAME);
221
                url, TEST_USERNAME, TEST_PASSWORD, TEST_DISPLAY_NAME);
222
        assertEquals(TEST_PASSWORD, getPasswordFromKeyring("j2eeserver:" + url));
222
        assertEquals(TEST_PASSWORD, new String(Keyring.read("j2eeserver:" + url)));
223
223
224
        ServerRegistry.getInstance().removeServerInstance(url);
224
        ServerRegistry.getInstance().removeServerInstance(url);
225
        assertNull(getPasswordFromKeyring("j2eeserver:" + url));
225
        assertNull(Keyring.read("j2eeserver:" + url));
226
    }
227
228
    private static String getPasswordFromKeyring(final String key) throws Exception {
229
        Future<String> ret = getKeyringAccess().submit(new Callable<String>() {
230
231
            @Override
232
            public String call() throws Exception {
233
                char[] passwd = Keyring.read(key);
234
                if (passwd != null) {
235
                    return String.valueOf(passwd);
236
                }
237
                return null;
238
            }
239
        });
240
        return ret.get(5, TimeUnit.SECONDS);
241
    }
242
243
    private static RequestProcessor getKeyringAccess() throws Exception {
244
        Field field = ServerRegistry.class.getDeclaredField("KEYRING_ACCESS");
245
        field.setAccessible(true);
246
        return (RequestProcessor) field.get(null);
247
    }
226
    }
248
227
249
    private static void assertPropertiesEquals(Map<String, String> expected, InstanceProperties props) {
228
    private static void assertPropertiesEquals(Map<String, String> expected, InstanceProperties props) {
(-)a/keyring/apichanges.xml (+29 lines)
Lines 49-54 Link Here
49
        <apidef name="keyring">Keyring API</apidef>
49
        <apidef name="keyring">Keyring API</apidef>
50
    </apidefs>
50
    </apidefs>
51
    <changes>
51
    <changes>
52
        <change id="edt">
53
            <api name="keyring"/>
54
            <summary>Keyring API usable from any thread</summary>
55
            <version major="1" minor="10"/>
56
            <date day="6" month="1" year="2012"/>
57
            <author login="phejl"/>
58
            <compatibility>
59
                <p>
60
                    Existing callers should use the Keyring directly from
61
                    any thread and should not try to avoid EDT anymore.
62
                </p>
63
                <p>
64
                    SPI implementors should not be changed and they may
65
                    continue to assume that they will not be called directly
66
                    from EDT.
67
                </p>
68
            </compatibility>
69
            <description>
70
                <p>
71
                    It hasn't been allowed to call the Keyring from EDT.
72
                    This change removes the limitation as the need to read
73
                    password from the UI is not so rare. To resolve this people
74
                    had to code custom threading solution to prevent possible
75
                    deadlock on fallback implementation of the keyring API.
76
                </p>
77
            </description>
78
            <class package="org.netbeans.api.keyring" name="Keyring"/>
79
            <issue number="206475"/>
80
        </change>
52
        <change id="initial">
81
        <change id="initial">
53
            <api name="keyring"/>
82
            <api name="keyring"/>
54
            <summary>Keyring API created</summary>
83
            <summary>Keyring API created</summary>
(-)a/keyring/arch.xml (-1 / +2 lines)
Lines 612-618 Link Here
612
-->
612
-->
613
 <answer id="exec-threading">
613
 <answer id="exec-threading">
614
  <p>
614
  <p>
615
   XXX no answer for exec-threading
615
   The API methods may be called from any thread. The SPI implementors may
616
   assume that they will not be called directly from EDT.
616
  </p>
617
  </p>
617
 </answer>
618
 </answer>
618
619
(-)a/keyring/manifest.mf (-1 / +1 lines)
Lines 1-6 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.keyring
2
OpenIDE-Module: org.netbeans.modules.keyring
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/keyring/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/keyring/Bundle.properties
4
OpenIDE-Module-Specification-Version: 1.9
4
OpenIDE-Module-Specification-Version: 1.10
5
OpenIDE-Module-Recommends: org.netbeans.modules.keyring.impl
5
OpenIDE-Module-Recommends: org.netbeans.modules.keyring.impl
6
6
(-)a/keyring/nbproject/project.xml (+18 lines)
Lines 6-11 Link Here
6
            <code-name-base>org.netbeans.modules.keyring</code-name-base>
6
            <code-name-base>org.netbeans.modules.keyring</code-name-base>
7
            <module-dependencies>
7
            <module-dependencies>
8
                <dependency>
8
                <dependency>
9
                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
10
                    <build-prerequisite/>
11
                    <compile-dependency/>
12
                    <run-dependency>
13
                        <release-version>1</release-version>
14
                        <specification-version>1.13</specification-version>
15
                    </run-dependency>
16
                </dependency>
17
                <dependency>
18
                    <code-name-base>org.netbeans.api.progress</code-name-base>
19
                    <build-prerequisite/>
20
                    <compile-dependency/>
21
                    <run-dependency>
22
                        <release-version>1</release-version>
23
                        <specification-version>1.27</specification-version>
24
                    </run-dependency>
25
                </dependency>
26
                <dependency>
9
                    <code-name-base>org.openide.util</code-name-base>
27
                    <code-name-base>org.openide.util</code-name-base>
10
                    <build-prerequisite/>
28
                    <build-prerequisite/>
11
                    <compile-dependency/>
29
                    <compile-dependency/>
(-)a/keyring/src/org/netbeans/api/keyring/Keyring.java (-14 / +132 lines)
Lines 45-65 Link Here
45
import java.util.Arrays;
45
import java.util.Arrays;
46
import java.util.HashMap;
46
import java.util.HashMap;
47
import java.util.Map;
47
import java.util.Map;
48
import java.util.concurrent.Callable;
49
import java.util.concurrent.ExecutionException;
50
import java.util.concurrent.Future;
51
import java.util.concurrent.TimeUnit;
52
import java.util.concurrent.TimeoutException;
48
import java.util.logging.Level;
53
import java.util.logging.Level;
49
import java.util.logging.Logger;
54
import java.util.logging.Logger;
55
import javax.swing.SwingUtilities;
56
import org.netbeans.api.annotations.common.CheckForNull;
57
import org.netbeans.api.annotations.common.NonNull;
58
import org.netbeans.api.annotations.common.NullAllowed;
59
import org.netbeans.api.progress.ProgressHandle;
60
import org.netbeans.api.progress.ProgressUtils;
50
import org.netbeans.spi.keyring.KeyringProvider;
61
import org.netbeans.spi.keyring.KeyringProvider;
62
import org.openide.util.Cancellable;
51
import org.openide.util.Lookup;
63
import org.openide.util.Lookup;
64
import org.openide.util.NbBundle;
52
import org.openide.util.Parameters;
65
import org.openide.util.Parameters;
66
import org.openide.util.RequestProcessor;
53
67
54
/**
68
/**
55
 * Client class for working with stored keys (such as passwords).
69
 * Client class for working with stored keys (such as passwords).
56
 * <p>The key identifier should be unique for the whole application,
70
 * <p>The key identifier should be unique for the whole application,
57
 * so qualify it with any prefixes as needed.
71
 * so qualify it with any prefixes as needed.
58
 * <p>Avoid calling methods on this class from the event dispatch thread,
72
 * <p> <i>Since 1.10</i> it is allowed to call methods of this class from even
59
 * as some provider implementations may need to block while displaying a dialog
73
 * dispatch thread.
60
 * (e.g. prompting for a master password to access the keyring).
61
 */
74
 */
62
public class Keyring {
75
public final class Keyring {
76
77
    // throughput 1 is intentional
78
    private static final RequestProcessor KEYRING_ACCESS = new RequestProcessor(Keyring.class);
79
80
    private static final long SAFE_DELAY = 70;
63
81
64
    private Keyring() {}
82
    private Keyring() {}
65
83
Lines 82-124 Link Here
82
        return PROVIDER;
100
        return PROVIDER;
83
    }
101
    }
84
102
103
    private static synchronized char[] readImpl(String key) {
104
        LOG.log(Level.FINEST, "reading: {0}", key);
105
        return provider().read(key);
106
    }
107
85
    /**
108
    /**
86
     * Reads a key from the ring.
109
     * Reads a key from the ring.
110
     * <p>
111
     * This method can be called from any thread.
112
     * All the changes done by previous calls to {@link #delete(java.lang.String)}
113
     * or {@link #save(java.lang.String, char[], java.lang.String)} methods
114
     * are guaranteed to be visible by subsequent calls to this method.
115
     *
87
     * @param key the identifier of the key
116
     * @param key the identifier of the key
88
     * @return its value if found (you may null out its elements), else null if not present
117
     * @return its value if found (you may null out its elements), else null if not present
89
     */
118
     */
90
    public static synchronized char[] read(String key) {
119
    @NbBundle.Messages("MSG_KeyringAccess=Requesting keyring access")
120
    @CheckForNull
121
    public static char[] read(@NonNull final String key) {
91
        Parameters.notNull("key", key);
122
        Parameters.notNull("key", key);
92
        LOG.log(Level.FINEST, "reading: {0}", key);
123
93
        return provider().read(key);
124
        try {
125
            final Future<char[]> result = KEYRING_ACCESS.submit(new Callable<char[]>() {
126
                @Override
127
                public char[] call() throws Exception {
128
                    return Keyring.readImpl(key);
129
                }
130
            });
131
132
            if (SwingUtilities.isEventDispatchThread()) {
133
                if (!result.isDone()) {
134
                    try {
135
                        // lets wait in awt to avoid flashing dialogs
136
                        return result.get(SAFE_DELAY, TimeUnit.MILLISECONDS);
137
                    } catch (TimeoutException ex) {
138
                        // show progress dialog
139
                        return ProgressUtils.showProgressDialogAndRun(
140
                                new ProgressRunnable<char[]>(result), Bundle.MSG_KeyringAccess(), false);
141
                    }
142
                }
143
            }
144
            return result.get();
145
        } catch (InterruptedException ex) {
146
            Thread.currentThread().interrupt();
147
        } catch (ExecutionException ex) {
148
            LOG.log(Level.INFO, null, ex);
149
        }
150
        return null;
151
    }
152
153
    private static synchronized void saveImpl(String key, char[] password, String description) {
154
        LOG.log(Level.FINEST, "saving: {0}", key);
155
        provider().save(key, password, description);
156
        Arrays.fill(password, (char) 0);
94
    }
157
    }
95
158
96
    /**
159
    /**
97
     * Saves a key to the ring.
160
     * Saves a key to the ring.
98
     * If it could not be saved, does nothing.
161
     * If it could not be saved, does nothing.
99
     * If the key already existed, overwrites the password.
162
     * If the key already existed, overwrites the password.
163
     * <p>
164
     * This method can be called from any thread.
165
     * The changes done by multiple calls to {@link #delete(java.lang.String)}
166
     * or {@link #save(java.lang.String, char[], java.lang.String)} methods
167
     * are guaranteed to be processed in order in which they were called.
168
     *
100
     * @param key a key identifier
169
     * @param key a key identifier
101
     * @param password the password or other sensitive information associated with the key
170
     * @param password the password or other sensitive information associated with the key
102
     *                 (its contents will be nulled out by end of call)
171
     *                 (its contents will be nulled out by end of call)
103
     * @param description a user-visible description of the key (may be null)
172
     * @param description a user-visible description of the key (may be null)
104
     */
173
     */
105
    public static synchronized void save(String key, char[] password, String description) {
174
    public static void save(@NonNull final String key, @NonNull final char[] password,
175
            @NullAllowed final String description) {
176
106
        Parameters.notNull("key", key);
177
        Parameters.notNull("key", key);
107
        Parameters.notNull("password", password);
178
        Parameters.notNull("password", password);
108
        LOG.log(Level.FINEST, "saving: {0}", key);
179
109
        provider().save(key, password, description);
180
        KEYRING_ACCESS.post(new Runnable() {
110
        Arrays.fill(password, (char) 0);
181
182
            @Override
183
            public void run() {
184
                Keyring.saveImpl(key, password, description);
185
            }
186
        });
187
    }
188
189
    private static synchronized void deleteImpl(String key) {
190
        LOG.log(Level.FINEST, "deleting: {0}", key);
191
        provider().delete(key);
111
    }
192
    }
112
193
113
    /**
194
    /**
114
     * Deletes a key from the ring.
195
     * Deletes a key from the ring.
115
     * If the key was not in the ring to begin with, does nothing.
196
     * If the key was not in the ring to begin with, does nothing.
197
     * <p>
198
     * This method can be called from any thread.
199
     * The changes done by multiple calls to {@link #delete(java.lang.String)}
200
     * or {@link #save(java.lang.String, char[], java.lang.String)} methods
201
     * are guaranteed to be processed in order in which they were called.
202
     *
116
     * @param key a key identifier
203
     * @param key a key identifier
117
     */
204
     */
118
    public static synchronized void delete(String key) {
205
    public static void delete(@NonNull final String key) {
119
        Parameters.notNull("key", key);
206
        Parameters.notNull("key", key);
120
        LOG.log(Level.FINEST, "deleting: {0}", key);
207
121
        provider().delete(key);
208
        KEYRING_ACCESS.post(new Runnable() {
209
210
            @Override
211
            public void run() {
212
                Keyring.deleteImpl(key);
213
            }
214
        });
122
    }
215
    }
123
216
124
    private static class DummyKeyringProvider implements KeyringProvider {
217
    private static class DummyKeyringProvider implements KeyringProvider {
Lines 156-159 Link Here
156
        return result;
249
        return result;
157
    }
250
    }
158
251
252
    private static class ProgressRunnable<T> implements org.netbeans.api.progress.ProgressRunnable<T>, Cancellable {
253
254
        private final Future<? extends T> task;
255
256
        public ProgressRunnable(Future<? extends T> task) {
257
            this.task = task;
258
        }
259
260
        @Override
261
        public T run(ProgressHandle handle) {
262
            try {
263
                return task.get();
264
            } catch (InterruptedException ex) {
265
                Thread.currentThread().interrupt();
266
            } catch (ExecutionException ex) {
267
                LOG.log(Level.INFO, null, ex);
268
            }
269
            return null;
270
        }
271
272
        @Override
273
        public boolean cancel() {
274
            return task.cancel(true);
275
        }
276
    }
159
}
277
}
(-)a/php.project/nbproject/project.xml (-1 / +1 lines)
Lines 177-183 Link Here
177
                    <build-prerequisite/>
177
                    <build-prerequisite/>
178
                    <compile-dependency/>
178
                    <compile-dependency/>
179
                    <run-dependency>
179
                    <run-dependency>
180
                        <specification-version>1.0</specification-version>
180
                        <specification-version>1.10</specification-version>
181
                    </run-dependency>
181
                    </run-dependency>
182
                </dependency>
182
                </dependency>
183
                <dependency>
183
                <dependency>
(-)a/php.project/src/org/netbeans/modules/php/project/connections/spi/Bundle.properties (-1 lines)
Lines 42-45 Link Here
42
# 0 - name of configuration
42
# 0 - name of configuration
43
# 1 - type of a configuration (FTP etc.)
43
# 1 - type of a configuration (FTP etc.)
44
MSG_PasswordFor=Password for {0} [{1}]
44
MSG_PasswordFor=Password for {0} [{1}]
45
MSG_KeyringAccess=Requesting keyring access
(-)a/php.project/src/org/netbeans/modules/php/project/connections/spi/RemoteConfiguration.java (-69 / +16 lines)
Lines 42-62 Link Here
42
42
43
package org.netbeans.modules.php.project.connections.spi;
43
package org.netbeans.modules.php.project.connections.spi;
44
44
45
import java.util.concurrent.Callable;
46
import java.util.concurrent.ExecutionException;
47
import java.util.concurrent.Future;
48
import java.util.concurrent.TimeUnit;
49
import java.util.concurrent.TimeoutException;
50
import java.util.logging.Level;
45
import java.util.logging.Level;
51
import java.util.logging.Logger;
46
import java.util.logging.Logger;
52
import javax.swing.SwingUtilities;
53
import org.netbeans.api.keyring.Keyring;
47
import org.netbeans.api.keyring.Keyring;
54
import org.netbeans.api.progress.ProgressUtils;
55
import org.netbeans.modules.php.api.util.StringUtils;
48
import org.netbeans.modules.php.api.util.StringUtils;
56
import org.netbeans.modules.php.project.connections.ConfigManager;
49
import org.netbeans.modules.php.project.connections.ConfigManager;
57
import org.netbeans.modules.php.project.util.PhpProjectUtils;
50
import org.netbeans.modules.php.project.util.PhpProjectUtils;
58
import org.openide.util.NbBundle;
51
import org.openide.util.NbBundle;
59
import org.openide.util.RequestProcessor;
60
52
61
/**
53
/**
62
 * Class representing a remote configuration (e.g. FTP, SFTP).
54
 * Class representing a remote configuration (e.g. FTP, SFTP).
Lines 69-75 Link Here
69
public abstract class RemoteConfiguration {
61
public abstract class RemoteConfiguration {
70
62
71
    protected static final Logger LOGGER = Logger.getLogger(RemoteConfiguration.class.getName());
63
    protected static final Logger LOGGER = Logger.getLogger(RemoteConfiguration.class.getName());
72
    static final RequestProcessor KEYRING_ACCESS = new RequestProcessor();
73
64
74
    protected final ConfigManager.Configuration cfg;
65
    protected final ConfigManager.Configuration cfg;
75
66
Lines 301-349 Link Here
301
    }
292
    }
302
293
303
    private String readPasswordFromKeyring() {
294
    private String readPasswordFromKeyring() {
304
        try {
295
        // new password key
305
            final Future<String> result = KEYRING_ACCESS.submit(new Callable<String>() {
296
        char[] newPassword = Keyring.read(passwordKey);
306
                @Override
297
        if (newPassword != null) {
307
                public String call() throws Exception {
298
            return new String(newPassword);
308
                    // new password key
299
        }
309
                    char[] newPassword = Keyring.read(passwordKey);
300
        // deprecated password key
310
                    if (newPassword != null) {
301
        newPassword = Keyring.read(deprecatedPasswordKey);
311
                        return new String(newPassword);
302
        if (newPassword != null) {
312
                    }
303
            return new String(newPassword);
313
                    // deprecated password key
314
                    newPassword = Keyring.read(deprecatedPasswordKey);
315
                    if (newPassword != null) {
316
                        return new String(newPassword);
317
                    }
318
                    return null;
319
                }
320
            });
321
            if (SwingUtilities.isEventDispatchThread()) {
322
                if (!result.isDone()) {
323
                    try {
324
                        // let's wait in awt to avoid flashing dialogs
325
                        result.get(99, TimeUnit.MILLISECONDS);
326
                    } catch (TimeoutException ex) {
327
                        ProgressUtils.showProgressDialogAndRun(new Runnable() {
328
                            @Override
329
                            public void run() {
330
                                try {
331
                                    result.get();
332
                                } catch (InterruptedException ex) {
333
                                    Thread.currentThread().interrupt();
334
                                } catch (ExecutionException ex) {
335
                                    LOGGER.log(Level.INFO, null, ex);
336
                                }
337
                            }
338
                        }, NbBundle.getMessage(RemoteConfiguration.class, "MSG_KeyringAccess"));
339
                    }
340
                }
341
            }
342
            return result.get();
343
        } catch (InterruptedException ex) {
344
            Thread.currentThread().interrupt();
345
        } catch (ExecutionException ex) {
346
            LOGGER.log(Level.INFO, null, ex);
347
        }
304
        }
348
        return null;
305
        return null;
349
    }
306
    }
Lines 355-383 Link Here
355
            return;
312
            return;
356
        }
313
        }
357
        if (StringUtils.hasText(password)) {
314
        if (StringUtils.hasText(password)) {
358
            KEYRING_ACCESS.post(new Runnable() {
315
            Keyring.save(passwordKey, password.toCharArray(),
359
                @Override
316
                    NbBundle.getMessage(RemoteConfiguration.class, "MSG_PasswordFor", getDisplayName(), type));
360
                public void run() {
317
            // remove old password key
361
                    Keyring.save(passwordKey, password.toCharArray(),
318
            Keyring.delete(deprecatedPasswordKey);
362
                            NbBundle.getMessage(RemoteConfiguration.class, "MSG_PasswordFor", getDisplayName(), type));
363
                    // remove old password key
364
                    Keyring.delete(deprecatedPasswordKey);
365
                }
366
            });
367
        } else {
319
        } else {
368
            deletePassword();
320
            deletePassword();
369
        }
321
        }
370
    }
322
    }
371
323
372
    protected void deletePassword() {
324
    protected void deletePassword() {
373
        KEYRING_ACCESS.post(new Runnable() {
325
        Keyring.delete(passwordKey);
374
            @Override
326
        // remove old password key
375
            public void run() {
327
        Keyring.delete(deprecatedPasswordKey);
376
                Keyring.delete(passwordKey);
377
                // remove old password key
378
                Keyring.delete(deprecatedPasswordKey);
379
            }
380
        });
381
    }
328
    }
382
329
383
}
330
}

Return to bug 206475