You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2020/08/10 09:08:21 UTC
[netbeans] branch master updated: Make it easy to create
dynamically updated ProxyLookup instances without subclassing
This is an automated email from the ASF dual-hosted git repository.
jtulach pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 2a4a18a Make it easy to create dynamically updated ProxyLookup instances without subclassing
2a4a18a is described below
commit 2a4a18a201fec8b66c167eef012004e2c4be21f3
Author: Tim Boudreau <ti...@timboudreau.com>
AuthorDate: Mon Aug 10 11:06:50 2020 +0200
Make it easy to create dynamically updated ProxyLookup instances without subclassing
---
platform/openide.util.lookup/apichanges.xml | 27 +++
platform/openide.util.lookup/manifest.mf | 2 +-
.../src/org/openide/util/lookup/ProxyLookup.java | 184 +++++++++++++------
.../util/lookup/ProxyLookupFactoryMethodsTest.java | 196 +++++++++++++++++++++
4 files changed, 358 insertions(+), 51 deletions(-)
diff --git a/platform/openide.util.lookup/apichanges.xml b/platform/openide.util.lookup/apichanges.xml
index 99c701f..f3d71c3 100644
--- a/platform/openide.util.lookup/apichanges.xml
+++ b/platform/openide.util.lookup/apichanges.xml
@@ -25,6 +25,33 @@
<apidef name="lookup">Lookup API</apidef>
</apidefs>
<changes>
+ <change id="ProxyLookupController">
+ <api name="lookup"/>
+ <summary>Add ProxyLookup.Controller to set lookups dynamically without
+ subclassing</summary>
+ <version major="8" minor="43"/>
+ <date year="2020" month="7" day="4"/>
+ <author login="tboudreau"/>
+ <compatibility addition="yes" source="compatible" semantic="compatible" binary="compatible"/>
+ <description>
+ <p>
+ One of the most common usages of <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ is to dynamically change the set of lookups being delegated to. However the
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html#setLookups-org.openide.util.Lookup...-">setLookups(...)</a>
+ method is <code>protected</code>. To avoid the need to subclass
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ this change introduces
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.Controller.html">ProxyLookup.Controller</a>
+ that gives the creator of <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>
+ a way to call
+ <a href="@TOP@/org/openide/util/lookup/ProxyLookup.Controller.html#setLookups-org.openide.util.Lookup...-">setLookups(...)</a>
+ without exposing the method to others having just a reference to
+ the <a href="@TOP@/org/openide/util/lookup/ProxyLookup.html">ProxyLookup</a>.
+ </p>
+ </description>
+ <class name="ProxyLookup" package="org.openide.util.lookup"/>
+ <issue number="NETBEANS-4699"/>
+ </change>
<change id="AbstractProcessorSupportedSource">
<api name="lookup"/>
<summary>Declare support for all source levels.</summary>
diff --git a/platform/openide.util.lookup/manifest.mf b/platform/openide.util.lookup/manifest.mf
index 7b0558c..372496f 100644
--- a/platform/openide.util.lookup/manifest.mf
+++ b/platform/openide.util.lookup/manifest.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.openide.util.lookup
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
-OpenIDE-Module-Specification-Version: 8.42
+OpenIDE-Module-Specification-Version: 8.43
diff --git a/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java b/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
index e18d04d..17cb46e 100644
--- a/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
+++ b/platform/openide.util.lookup/src/org/openide/util/lookup/ProxyLookup.java
@@ -57,6 +57,25 @@ public class ProxyLookup extends Lookup {
public ProxyLookup(Lookup... lookups) {
data = ImmutableInternalData.EMPTY.setLookupsNoFire(lookups, true);
}
+ /**
+ * Create a {@code ProxyLookup} whose contents can be set dynamically
+ * subclassing. The passed
+ * {@link Controller} can be later be used to call
+ * {@link Controller#setLookups} which then
+ * {@link ProxyLookup#setLookups changes} the lookups this {@code ProxyLookup}
+ * delegates to. The passed controller may
+ * only be used for <i>one</i> ProxyLookup.
+ *
+ * @param controller A {@link Controller} which can be used to set the lookups
+ * @throws IllegalStateException if the passed controller has already
+ * been attached to another ProxyLookup
+ * @since 8.43
+ */
+ @SuppressWarnings("LeakingThisInConstructor")
+ public ProxyLookup(Controller controller) {
+ this();
+ controller.setProxyLookup(this);
+ }
/**
* Create a lookup initially proxying to no others.
@@ -89,7 +108,72 @@ public class ProxyLookup extends Lookup {
}
return map.keySet();
}
-
+
+ /**
+ * A controller which allows the set of lookups being proxied to be
+ * set dynamically for those who create the instance of
+ * {@link ProxyLookup}.
+ *
+ * @since 8.43
+ */
+ public static final class Controller {
+
+ private ProxyLookup consumer;
+
+ /**
+ * Creates a new controller to be attached to a {@link ProxyLookup}.
+ * @since 8.43
+ */
+ public Controller() {
+ }
+
+ /**
+ * Set the lookups on the {@link ProxyLookup} this controller controls.
+ * If called before a {@link ProxyLookup} has been attached to this
+ * controller, an IllegalStateException will be thrown.
+ *
+ * @param notifyIn an executor to notify changes in
+ * @param lookups an array of Lookups to be proxied
+ * @throws IllegalStateException if called before this instance
+ * has been passed to the constructor of (exactly one) {@link ProxyLookup}
+ * @since 8.43
+ */
+ public void setLookups(Executor notifyIn, Lookup... lookups) {
+ if (consumer == null) {
+ throw new IllegalStateException("Cannot use Controller until "
+ + "a ProxyLookup has been created with it.");
+ }
+ consumer.setLookups(notifyIn, lookups);
+ }
+
+ /**
+ * Set the lookups on the {@link ProxyLookup} this controller controls.
+ * If called before a {@link ProxyLookup} has been attached to this
+ * controller, an IllegalStateException will be thrown.
+ *
+ * @param exe An executor to notify in
+ * @param lookups An array of Lookups to be proxied
+ * @throws IllegalStateException if called before this instance
+ * has been passed to the constructor of (exactly one) {@link ProxyLookup}
+ * @since 8.43
+ */
+ public void setLookups(Lookup... lookups) {
+ if (consumer == null) {
+ throw new IllegalStateException("Cannot use Controller until "
+ + "a ProxyLookup has been created with it.");
+ }
+ setLookups(null, lookups);
+ }
+
+ void setProxyLookup(ProxyLookup lkp) {
+ if (consumer != null) {
+ throw new IllegalStateException("Controller cannot be used "
+ + "with more than one ProxyLookup.");
+ }
+ consumer = lkp;
+ }
+ }
+
/**
* Changes the delegates.
*
@@ -99,7 +183,7 @@ public class ProxyLookup extends Lookup {
protected final void setLookups(Lookup... lookups) {
setLookups(null, lookups);
}
-
+
/**
* Changes the delegates immediatelly, notifies the listeners in provided
* executor, potentially later.
@@ -113,10 +197,10 @@ public class ProxyLookup extends Lookup {
Set<Lookup> newL;
Set<Lookup> current;
Lookup[] old;
-
+
Map<Result,LookupListener> toRemove = new IdentityHashMap<Lookup.Result, LookupListener>();
Map<Result,LookupListener> toAdd = new IdentityHashMap<Lookup.Result, LookupListener>();
-
+
ImmutableInternalData orig;
synchronized (ProxyLookup.this) {
orig = getData();
@@ -126,7 +210,7 @@ public class ProxyLookup extends Lookup {
}
arr = setData(newData, lookups, toAdd, toRemove);
}
-
+
// better to do this later than in synchronized block
for (Map.Entry<Result, LookupListener> e : toRemove.entrySet()) {
e.getKey().removeLookupListener(e.getValue());
@@ -144,7 +228,7 @@ public class ProxyLookup extends Lookup {
r.collectFires(evAndListeners);
}
}
-
+
class Notify implements Runnable {
public void run() {
Iterator it = evAndListeners.iterator();
@@ -208,7 +292,7 @@ public class ProxyLookup extends Lookup {
public final <T> Item<T> lookupItem(Template<T> template) {
beforeLookup(template);
- Lookup[] tmpLkps;
+ Lookup[] tmpLkps;
synchronized (ProxyLookup.this) {
tmpLkps = getData().getLookups(false);
}
@@ -256,14 +340,14 @@ public class ProxyLookup extends Lookup {
}
private Collection<Reference<R>> setData(
- ImmutableInternalData newData, Lookup[] current,
+ ImmutableInternalData newData, Lookup[] current,
Map<Result,LookupListener> toAdd, Map<Result,LookupListener> toRemove
) {
assert Thread.holdsLock(ProxyLookup.this);
assert newData != null;
-
+
ImmutableInternalData previous = this.getData();
-
+
if (previous == newData) {
return Collections.emptyList();
}
@@ -314,14 +398,14 @@ public class ProxyLookup extends Lookup {
private static final class R<T> extends WaitableResult<T> {
/** weak listener & result */
private final WeakResult<T> weakL;
-
+
/** list of listeners added */
private LookupListenerList listeners;
/** collection of Objects */
private Collection[] cache;
-
+
/** associated lookup */
private ImmutableInternalData data;
@@ -330,7 +414,7 @@ public class ProxyLookup extends Lookup {
public R(ProxyLookup proxy, Lookup.Template<T> t) {
this.weakL = new WeakResult<T>(proxy, this, t);
}
-
+
private ProxyLookup proxy() {
return weakL.result.proxy;
}
@@ -339,7 +423,7 @@ public class ProxyLookup extends Lookup {
private Result<T>[] newResults(int len) {
return new Result[len];
}
-
+
@Override
protected void finalize() {
weakL.result.run();
@@ -369,7 +453,7 @@ public class ProxyLookup extends Lookup {
if (current != data) {
continue;
}
-
+
Lookup[] currentLkps = data.getLookups(false);
if (currentLkps.length != myLkps.length) {
continue BIG_LOOP;
@@ -379,8 +463,8 @@ public class ProxyLookup extends Lookup {
continue BIG_LOOP;
}
}
-
- // some other thread might compute the result mean while.
+
+ // some other thread might compute the result mean while.
// if not finish the computation yourself
if (weakL.getResults() != null) {
return weakL.getResults();
@@ -548,7 +632,7 @@ public class ProxyLookup extends Lookup {
public void resultChanged(LookupEvent ev) {
collectFires(null);
}
-
+
private static ThreadLocal<R<?>> IN = new ThreadLocal<>();
protected void collectFires(Collection<Object> evAndListeners) {
R<?> prev = IN.get();
@@ -563,7 +647,7 @@ public class ProxyLookup extends Lookup {
IN.set(prev);
}
}
-
+
private void collImpl(Collection<Object> evAndListeners) {
boolean modified = true;
@@ -589,7 +673,7 @@ public class ProxyLookup extends Lookup {
}
ll = listeners.getListenerList();
assert ll != null;
-
+
// ignore events if they arrive as a result of call to allItems
// or allInstances, bellow...
@@ -632,7 +716,7 @@ public class ProxyLookup extends Lookup {
}
}
}
-
+
if (modified) {
LookupEvent ev = new LookupEvent(this);
AbstractLookup.notifyListeners(ll, ev, evAndListeners);
@@ -646,7 +730,7 @@ public class ProxyLookup extends Lookup {
boolean callBeforeLookup, boolean callBeforeOnWait
) {
Template<T> template = template();
-
+
proxy().beforeLookup(callBeforeLookup, template);
Lookup.Result<T>[] arr = initResults();
@@ -691,11 +775,11 @@ public class ProxyLookup extends Lookup {
synchronized (proxy()) {
Collection[] cc = getCache();
if (cc != oldCC) {
- // don't change the cache when it is based on
+ // don't change the cache when it is based on
// outdated results
return;
}
-
+
if (cc == null || cc == R.NO_CACHE) {
// initialize the cache to indicate this result is in use
setCache(cc = new Collection[3]);
@@ -707,14 +791,14 @@ public class ProxyLookup extends Lookup {
cc[indexToCache] = ret;
}
}
-
+
}
}
private static final class WeakRef<T> extends WeakReference<R> implements Runnable {
final WeakResult<T> result;
final ProxyLookup proxy;
final Template<T> template;
-
+
public WeakRef(R r, WeakResult<T> result, ProxyLookup proxy, Template<T> template) {
super(r);
this.result = result;
@@ -727,17 +811,17 @@ public class ProxyLookup extends Lookup {
proxy.unregisterTemplate(template);
}
}
-
-
+
+
private static final class WeakResult<T> extends WaitableResult<T> implements LookupListener, Runnable {
/** all results */
private Lookup.Result<T>[] results;
private final WeakRef<T> result;
-
+
public WeakResult(ProxyLookup proxy, R r, Template<T> t) {
this.result = new WeakRef<T>(r, this, proxy, t);
}
-
+
final void removeListeners() {
Lookup.Result<T>[] arr = this.getResults();
if (arr == null) {
@@ -822,15 +906,15 @@ public class ProxyLookup extends Lookup {
return allItems();
}
} // end of WeakResult
-
+
static abstract class ImmutableInternalData extends Object {
static final ImmutableInternalData EMPTY = new EmptyInternalData();
static final Lookup[] EMPTY_ARR = new Lookup[0];
-
+
protected ImmutableInternalData() {
}
-
+
public static ImmutableInternalData create(Object lkp, Map<Template, Reference<R>> results) {
if (results.size() == 0 && lkp == EMPTY_ARR) {
return EMPTY;
@@ -839,7 +923,7 @@ public class ProxyLookup extends Lookup {
Entry<Template,Reference<R>> e = results.entrySet().iterator().next();
return new SingleInternalData(lkp, e.getKey(), e.getValue());
}
-
+
return new RealInternalData(lkp, results);
}
@@ -850,7 +934,7 @@ public class ProxyLookup extends Lookup {
final Collection<Reference<R>> references() {
return getResults().values();
}
-
+
final <T> ImmutableInternalData removeTemplate(ProxyLookup proxy, Template<T> template) {
if (getResults().containsKey(template)) {
HashMap<Template,Reference<R>> c = new HashMap<Template, Reference<ProxyLookup.R>>(getResults());
@@ -865,12 +949,12 @@ public class ProxyLookup extends Lookup {
return this;
}
}
-
+
<T> R<T> findResult(ProxyLookup proxy, ImmutableInternalData[] newData, Template<T> template) {
assert Thread.holdsLock(proxy);
-
+
Map<Template,Reference<R>> map = getResults();
-
+
Reference<R> ref = map.get(template);
R r = (ref == null) ? null : ref.get();
@@ -878,7 +962,7 @@ public class ProxyLookup extends Lookup {
newData[0] = this;
return convertResult(r);
}
-
+
HashMap<Template, Reference<R>> res = new HashMap<Template, Reference<R>>(map);
R<T> newR = new R<T>(proxy, template);
res.put(template, new java.lang.ref.SoftReference<R>(newR));
@@ -887,13 +971,13 @@ public class ProxyLookup extends Lookup {
}
final ImmutableInternalData setLookupsNoFire(Lookup[] lookups, boolean skipCheck) {
Object l;
-
+
if (!skipCheck) {
Lookup[] previous = getLookups(false);
if (previous == lookups) {
return this;
}
-
+
if (previous.length == lookups.length) {
int same = 0;
for (int i = 0; i < previous.length; i++) {
@@ -907,7 +991,7 @@ public class ProxyLookup extends Lookup {
}
}
}
-
+
if (lookups.length == 1) {
l = lookups[0];
assert l != null : "Cannot assign null delegate";
@@ -918,11 +1002,11 @@ public class ProxyLookup extends Lookup {
l = lookups.clone();
}
}
-
+
if (isEmpty() && l == EMPTY_ARR) {
return this;
}
-
+
return create(l, getResults());
}
final Lookup[] getLookups(boolean clone) {
@@ -938,17 +1022,17 @@ public class ProxyLookup extends Lookup {
}
}
final List<Lookup> getLookupsList() {
- return Arrays.asList(getLookups(false));
+ return Arrays.asList(getLookups(false));
}
} // end of ImmutableInternalData
-
+
private static final class SingleInternalData extends ImmutableInternalData {
/** lookups to delegate to (either Lookup or array of Lookups) */
private final Object lookups;
private final Template template;
private final Reference<ProxyLookup.R> result;
-
+
public SingleInternalData(Object lookups, Template<?> template, Reference<ProxyLookup.R> result) {
this.lookups = lookups;
this.template = template;
@@ -962,7 +1046,7 @@ public class ProxyLookup extends Lookup {
protected Map<Template, Reference<R>> getResults() {
return Collections.singletonMap(template, result);
}
-
+
protected Object getRawLookups() {
return lookups;
}
@@ -990,7 +1074,7 @@ public class ProxyLookup extends Lookup {
assert needsStrict = true;
return needsStrict && !isUnmodifiable(results) ? unmodifiableMap(results) : results;
}
-
+
@Override
protected Object getRawLookups() {
return lookups;
@@ -1008,7 +1092,7 @@ public class ProxyLookup extends Lookup {
return res;
}
}
-
+
private static final class EmptyInternalData extends ImmutableInternalData {
EmptyInternalData() {
}
diff --git a/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java b/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java
new file mode 100644
index 0000000..715e9f1
--- /dev/null
+++ b/platform/openide.util.lookup/test/unit/src/org/openide/util/lookup/ProxyLookupFactoryMethodsTest.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.openide.util.lookup;
+
+import java.util.Arrays;
+import static java.util.Arrays.asList;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.lookup.ProxyLookupFactoryMethodsTest.TThreadFactory.TThread;
+
+/**
+ *
+ * @author Tim Boudreau
+ */
+public class ProxyLookupFactoryMethodsTest {
+
+ private ProxyLookup.Controller controller1;
+ private ProxyLookup.Controller controller2;
+
+ private Lookup createWithSingleConsumer(Lookup... lookups) {
+ ProxyLookup.Controller controller = new ProxyLookup.Controller();
+ controller1 = controller;
+ ProxyLookup result = new ProxyLookup(controller);
+ result.setLookups(lookups);
+ return result;
+ }
+
+ private Lookup createWithBiConsumer(Lookup... lookups) {
+ ProxyLookup.Controller controller = new ProxyLookup.Controller();
+ controller2 = controller;
+ ProxyLookup result = new ProxyLookup(controller);
+ result.setLookups(lookups);
+ return result;
+ }
+
+ @Test
+ public void testCannotUseControllerOnMultipleLookups() {
+ ProxyLookup.Controller ctrllr = new ProxyLookup.Controller();
+ ProxyLookup first = new ProxyLookup(ctrllr);
+ assertTrue(first.lookupAll(String.class).isEmpty());
+ try {
+ ProxyLookup second = new ProxyLookup(ctrllr);
+ fail("Exception should have been thrown using controller more than "
+ + "once but was able to create " + second);
+ } catch (IllegalStateException ex) {
+ // ok
+ }
+ }
+
+ @Test
+ public void testStartWithEmptyController() {
+ ProxyLookup.Controller ctrllr = new ProxyLookup.Controller();
+ ProxyLookup lkp = new ProxyLookup(ctrllr);
+ assertTrue(lkp.lookupAll(String.class).isEmpty());
+ ctrllr.setLookups(Lookups.fixed("a"), Lookups.fixed("b"));
+ assertEquals(new HashSet<>(Arrays.asList("a", "b")),
+ new HashSet<>(lkp.lookupAll(String.class)));
+ }
+
+ @Test
+ public void testSimpleFactory() {
+ Lookup a = Lookups.fixed("a");
+ Lookup b = Lookups.fixed("b");
+ Lookup c = Lookups.fixed("c");
+
+ Lookup target = createWithSingleConsumer(a, b);
+ assertNotNull(controller1);
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b")));
+ assertFalse(target.lookupAll(String.class).contains("c"));
+
+ controller1.setLookups(new Lookup[]{a, b, c});
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b", "c")));
+
+ controller1.setLookups(new Lookup[0]);
+ assertTrue(target.lookupAll(String.class).isEmpty());
+ }
+
+ @Test
+ public void testThreadedFactory() throws Throwable {
+ ExecutorService svc = Executors.newSingleThreadExecutor(new TThreadFactory());
+ Lookup a = Lookups.fixed("a");
+ Lookup b = Lookups.fixed("b");
+ Lookup c = Lookups.fixed("c");
+
+ Lookup target = createWithBiConsumer(a, b);
+ Lookup.Result<String> result = target.lookupResult(String.class);
+
+ LL lis = new LL();
+ result.addLookupListener(lis);
+ // Ugh, ProxyLookup.LazyList does not implement the contract
+ // of Collection.equals().
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("a", "b")));
+
+ assertNotNull(controller2);
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b")));
+ assertFalse(target.lookupAll(String.class).contains("c"));
+
+ controller2.setLookups(svc, new Lookup[]{a, b, c});
+
+ lis.assertNotifiedInExecutor();
+
+ assertTrue(target.lookupAll(String.class).containsAll(asList("a", "b", "c")));
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("a", "b", "c")));
+
+ controller2.setLookups(svc, new Lookup[0]);
+ lis.assertNotifiedInExecutor();
+ assertTrue(target.lookupAll(String.class).isEmpty());
+ assertTrue(result.allInstances().isEmpty());
+
+ controller2.setLookups(null, new Lookup[]{b, c});
+ assertTrue(target.lookupAll(String.class).containsAll(asList("b", "c")));
+ assertEquals(new HashSet<>(result.allInstances()), new HashSet<>(Arrays.asList("b", "c")));
+ lis.assertNotifiedSynchronously();
+ }
+
+ static final class TThreadFactory implements ThreadFactory {
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new TThread(r);
+ }
+
+ static class TThread extends Thread {
+
+ TThread(Runnable r) {
+ super(r);
+ setName("test-thread");
+ setDaemon(true);
+ }
+ }
+ }
+
+ static class LL implements LookupListener {
+
+ private Thread notifyThread;
+ private CountDownLatch latch = new CountDownLatch(1);
+
+ void assertNotifiedInExecutor() throws InterruptedException {
+ CountDownLatch l;
+ synchronized (this) {
+ l = latch;
+ }
+ latch.await(10, TimeUnit.SECONDS);
+ Thread t;
+ synchronized (this) {
+ t = notifyThread;
+ notifyThread = null;
+ }
+ assertNotNull(t);
+ assertTrue(t instanceof TThread);
+ }
+
+ void assertNotifiedSynchronously() throws InterruptedException {
+ assertSame(Thread.currentThread(), notifyThread);
+ }
+
+ @Override
+ public void resultChanged(LookupEvent ev) {
+ CountDownLatch l;
+ synchronized (this) {
+ notifyThread = Thread.currentThread();
+ l = latch;
+ latch = new CountDownLatch(1);
+ assert l != null;
+ }
+ l.countDown();
+ }
+
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists