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/DatabaseConnection.java (-118 / +19 lines)
Lines 609-743 Link Here
609
            return ;
609
            return ;
610
        }
610
        }
611
        final String key = this.connectionFileName;
611
        final String key = this.connectionFileName;
612
        waitForReading(new Runnable() {
612
        // If the password was saved, then it means the user checked
613
            @Override
613
        // the box to say the password should be remembered.
614
            public void run() {
614
        char[] chars = Keyring.read(key);
615
                // If the password was saved, then it means the user checked
615
        if (chars != null) {
616
                // the box to say the password should be remembered.
616
            LOGGER.log(Level.FINE, "A password read for " + key);
617
                char[] chars = Keyring.read(key);
617
            pwd = String.valueOf(chars);
618
                if (chars != null) {
618
            rpwd = true;
619
                    LOGGER.log(Level.FINE, "A password read for " + key);
619
        } else {
620
                    pwd = String.valueOf(chars);
620
            LOGGER.log(Level.FINE, "No password read for " + key);
621
                    rpwd = true;
621
            pwd = "";
622
                } else {
622
            rpwd = false;
623
                    LOGGER.log(Level.FINE, "No password read for " + key);
623
        }
624
                    pwd = "";
625
                    rpwd = false;
626
                }
627
            }
628
            
629
        });
630
    }
624
    }
631
625
632
    public static void storePassword(final String key, final char[] pwd) {
626
    public static void storePassword(final String key, final char[] pwd) {
633
        Parameters.notNull("key", key);
627
        Parameters.notNull("key", key);
634
        Parameters.notNull("pwd", pwd);
628
        Parameters.notNull("pwd", pwd);
635
        RP.post(new Runnable() {
636
629
637
            @Override
630
        LOGGER.log(Level.FINE, "Storing password for " + key);
638
            public void run() {
631
        Keyring.save(key,
639
                LOGGER.log(Level.FINE, "Storing password for " + key);
632
                pwd,
640
                Keyring.save(key,
633
                NbBundle.getMessage(DatabaseConnectionConvertor.class,
641
                        pwd,
634
                    "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
    }
635
    }
730
    
636
    
731
    public static void deletePassword(final String key) {
637
    public static void deletePassword(final String key) {
732
        Parameters.notNull("key", key);
638
        Parameters.notNull("key", key);
733
        RP.post(new Runnable() {
734
639
735
            @Override
640
        LOGGER.log(Level.FINE, "Deleting password for " + key);
736
            public void run() {
641
        Keyring.delete(key);
737
                LOGGER.log(Level.FINE, "Deleting password for " + key);
738
                Keyring.delete(key);
739
            }
740
        });
741
    }
642
    }
742
    
643
    
743
    /** Returns if password should be remembered */
644
    /** 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/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 (+18 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
            <description>
60
                <p>
61
                    It hasn't been allowed to call the Keyring from EDT.
62
                    This change removes the limitation as the need to read
63
                    password from the UI is not so rare. To resolve this people
64
                    had to code custom threading solution to prevent possible
65
                    deadlock on fallback implementation of the keyring API.
66
                </p>
67
            </description>
68
            <issue number="206475"/>
69
        </change>
52
        <change id="initial">
70
        <change id="initial">
53
            <api name="keyring"/>
71
            <api name="keyring"/>
54
            <summary>Keyring API created</summary>
72
            <summary>Keyring API created</summary>
(-)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 / +113 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
@NbBundle.Messages("MSG_KeyringAccess=Requesting keyring access")
76
public final class Keyring {
77
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-96 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.
87
     * @param key the identifier of the key
110
     * @param key the identifier of the key
88
     * @return its value if found (you may null out its elements), else null if not present
111
     * @return its value if found (you may null out its elements), else null if not present
89
     */
112
     */
90
    public static synchronized char[] read(String key) {
113
    @CheckForNull
114
    public static char[] read(@NonNull final String key) {
91
        Parameters.notNull("key", key);
115
        Parameters.notNull("key", key);
92
        LOG.log(Level.FINEST, "reading: {0}", key);
116
93
        return provider().read(key);
117
        try {
118
            final Future<char[]> result = KEYRING_ACCESS.submit(new Callable<char[]>() {
119
                @Override
120
                public char[] call() throws Exception {
121
                    return Keyring.readImpl(key);
122
                }
123
            });
124
125
            if (SwingUtilities.isEventDispatchThread()) {
126
                if (!result.isDone()) {
127
                    try {
128
                        // lets wait in awt to avoid flashing dialogs
129
                        return result.get(SAFE_DELAY, TimeUnit.MILLISECONDS);
130
                    } catch (TimeoutException ex) {
131
                        // show progress dialog
132
                        return ProgressUtils.showProgressDialogAndRun(
133
                                new ProgressRunnable<char[]>(result), Bundle.MSG_KeyringAccess(), false);
134
                    }
135
                }
136
            }
137
            return result.get();
138
        } catch (InterruptedException ex) {
139
            Thread.currentThread().interrupt();
140
        } catch (ExecutionException ex) {
141
            LOG.log(Level.INFO, null, ex);
142
        }
143
        return null;
144
    }
145
146
    private static synchronized void saveImpl(String key, char[] password, String description) {
147
        LOG.log(Level.FINEST, "saving: {0}", key);
148
        provider().save(key, password, description);
149
        Arrays.fill(password, (char) 0);
94
    }
150
    }
95
151
96
    /**
152
    /**
Lines 102-113 Link Here
102
     *                 (its contents will be nulled out by end of call)
158
     *                 (its contents will be nulled out by end of call)
103
     * @param description a user-visible description of the key (may be null)
159
     * @param description a user-visible description of the key (may be null)
104
     */
160
     */
105
    public static synchronized void save(String key, char[] password, String description) {
161
    public static void save(@NonNull final String key, @NonNull final char[] password,
162
            @NullAllowed final String description) {
163
106
        Parameters.notNull("key", key);
164
        Parameters.notNull("key", key);
107
        Parameters.notNull("password", password);
165
        Parameters.notNull("password", password);
108
        LOG.log(Level.FINEST, "saving: {0}", key);
166
109
        provider().save(key, password, description);
167
        KEYRING_ACCESS.post(new Runnable() {
110
        Arrays.fill(password, (char) 0);
168
169
            @Override
170
            public void run() {
171
                Keyring.saveImpl(key, password, description);
172
            }
173
        });
174
    }
175
176
    private static synchronized void deleteImpl(String key) {
177
        LOG.log(Level.FINEST, "deleting: {0}", key);
178
        provider().delete(key);
111
    }
179
    }
112
180
113
    /**
181
    /**
Lines 115-124 Link Here
115
     * If the key was not in the ring to begin with, does nothing.
183
     * If the key was not in the ring to begin with, does nothing.
116
     * @param key a key identifier
184
     * @param key a key identifier
117
     */
185
     */
118
    public static synchronized void delete(String key) {
186
    public static void delete(@NonNull final String key) {
119
        Parameters.notNull("key", key);
187
        Parameters.notNull("key", key);
120
        LOG.log(Level.FINEST, "deleting: {0}", key);
188
121
        provider().delete(key);
189
        KEYRING_ACCESS.post(new Runnable() {
190
191
            @Override
192
            public void run() {
193
                Keyring.deleteImpl(key);
194
            }
195
        });
122
    }
196
    }
123
197
124
    private static class DummyKeyringProvider implements KeyringProvider {
198
    private static class DummyKeyringProvider implements KeyringProvider {
Lines 156-159 Link Here
156
        return result;
230
        return result;
157
    }
231
    }
158
232
233
    private static class ProgressRunnable<T> implements org.netbeans.api.progress.ProgressRunnable<T>, Cancellable {
234
235
        private final Future<? extends T> task;
236
237
        public ProgressRunnable(Future<? extends T> task) {
238
            this.task = task;
239
        }
240
241
        @Override
242
        public T run(ProgressHandle handle) {
243
            try {
244
                return task.get();
245
            } catch (InterruptedException ex) {
246
                Thread.currentThread().interrupt();
247
            } catch (ExecutionException ex) {
248
                LOG.log(Level.INFO, null, ex);
249
            }
250
            return null;
251
        }
252
253
        @Override
254
        public boolean cancel() {
255
            return task.cancel(true);
256
        }
257
    }
159
}
258
}
(-)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/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