org.openide.util
--- a/keyring/src/org/netbeans/api/keyring/Keyring.java Mon Dec 19 07:32:04 2011 +0100
+++ a/keyring/src/org/netbeans/api/keyring/Keyring.java Mon Dec 19 15:31:18 2011 +0100
@@ -45,21 +45,39 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressUtils;
import org.netbeans.spi.keyring.KeyringProvider;
+import org.openide.util.Cancellable;
import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
import org.openide.util.Parameters;
+import org.openide.util.RequestProcessor;
/**
* Client class for working with stored keys (such as passwords).
* The key identifier should be unique for the whole application,
* so qualify it with any prefixes as needed.
- *
Avoid calling methods on this class from the event dispatch thread,
- * as some provider implementations may need to block while displaying a dialog
- * (e.g. prompting for a master password to access the keyring).
+ *
Since 1.10 it is allowed to call methods of this class from even
+ * dispatch thread.
*/
-public class Keyring {
+@NbBundle.Messages("MSG_KeyringAccess=Requesting keyring access")
+public final class Keyring {
+
+ private static final RequestProcessor KEYRING_ACCESS = new RequestProcessor(Keyring.class);
+
+ private static final long SAFE_DELAY = 70;
private Keyring() {}
@@ -82,15 +100,53 @@
return PROVIDER;
}
+ private static synchronized char[] readImpl(String key) {
+ LOG.log(Level.FINEST, "reading: {0}", key);
+ return provider().read(key);
+ }
+
/**
* Reads a key from the ring.
* @param key the identifier of the key
* @return its value if found (you may null out its elements), else null if not present
*/
- public static synchronized char[] read(String key) {
+ @CheckForNull
+ public static char[] read(@NonNull final String key) {
Parameters.notNull("key", key);
- LOG.log(Level.FINEST, "reading: {0}", key);
- return provider().read(key);
+
+ try {
+ final Future result = KEYRING_ACCESS.submit(new Callable() {
+ @Override
+ public char[] call() throws Exception {
+ return Keyring.readImpl(key);
+ }
+ });
+
+ if (SwingUtilities.isEventDispatchThread()) {
+ if (!result.isDone()) {
+ try {
+ // lets wait in awt to avoid flashing dialogs
+ return result.get(SAFE_DELAY, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException ex) {
+ // show progress dialog
+ return ProgressUtils.showProgressDialogAndRun(
+ new ProgressRunnable(result), Bundle.MSG_KeyringAccess(), false);
+ }
+ }
+ }
+ return result.get();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ } catch (ExecutionException ex) {
+ LOG.log(Level.INFO, null, ex);
+ }
+ return null;
+ }
+
+ private static synchronized void saveImpl(String key, char[] password, String description) {
+ LOG.log(Level.FINEST, "saving: {0}", key);
+ provider().save(key, password, description);
+ Arrays.fill(password, (char) 0);
}
/**
@@ -102,12 +158,24 @@
* (its contents will be nulled out by end of call)
* @param description a user-visible description of the key (may be null)
*/
- public static synchronized void save(String key, char[] password, String description) {
+ public static void save(@NonNull final String key, @NonNull final char[] password,
+ @NullAllowed final String description) {
+
Parameters.notNull("key", key);
Parameters.notNull("password", password);
- LOG.log(Level.FINEST, "saving: {0}", key);
- provider().save(key, password, description);
- Arrays.fill(password, (char) 0);
+
+ KEYRING_ACCESS.post(new Runnable() {
+
+ @Override
+ public void run() {
+ Keyring.saveImpl(key, password, description);
+ }
+ });
+ }
+
+ private static synchronized void deleteImpl(String key) {
+ LOG.log(Level.FINEST, "deleting: {0}", key);
+ provider().delete(key);
}
/**
@@ -115,10 +183,16 @@
* If the key was not in the ring to begin with, does nothing.
* @param key a key identifier
*/
- public static synchronized void delete(String key) {
+ public static void delete(@NonNull final String key) {
Parameters.notNull("key", key);
- LOG.log(Level.FINEST, "deleting: {0}", key);
- provider().delete(key);
+
+ KEYRING_ACCESS.post(new Runnable() {
+
+ @Override
+ public void run() {
+ Keyring.deleteImpl(key);
+ }
+ });
}
private static class DummyKeyringProvider implements KeyringProvider {
@@ -156,4 +230,29 @@
return result;
}
+ private static class ProgressRunnable implements org.netbeans.api.progress.ProgressRunnable, Cancellable {
+
+ private final Future extends T> task;
+
+ public ProgressRunnable(Future extends T> task) {
+ this.task = task;
+ }
+
+ @Override
+ public T run(ProgressHandle handle) {
+ try {
+ return task.get();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ } catch (ExecutionException ex) {
+ LOG.log(Level.INFO, null, ex);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean cancel() {
+ return task.cancel(true);
+ }
+ }
}
--- a/php.project/nbproject/project.xml Mon Dec 19 07:32:04 2011 +0100
+++ a/php.project/nbproject/project.xml Mon Dec 19 15:31:18 2011 +0100
@@ -177,7 +177,7 @@
- 1.0
+ 1.10