Index: xtest/nbjunit/src/org/netbeans/junit/MockServices.java =================================================================== RCS file: /shared/data/ccvs/repository/xtest/nbjunit/src/org/netbeans/junit/MockServices.java,v retrieving revision 1.4 diff -u -r1.4 MockServices.java --- xtest/nbjunit/src/org/netbeans/junit/MockServices.java 12 May 2006 22:42:51 -0000 1.4 +++ xtest/nbjunit/src/org/netbeans/junit/MockServices.java 29 Jun 2006 15:40:02 -0000 @@ -19,7 +19,7 @@ import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLConnection; @@ -82,9 +82,9 @@ // Need to also reset global lookup since it caches the singleton and we need to change it. try { Class lookup = Class.forName("org.openide.util.Lookup"); - Field defaultLookup = lookup.getDeclaredField("defaultLookup"); + Method defaultLookup = lookup.getDeclaredMethod("resetDefaultLookup"); defaultLookup.setAccessible(true); - defaultLookup.set(null, null); + defaultLookup.invoke(null); } catch (ClassNotFoundException x) { // Fine, not using org-openide-lookup.jar. } catch (Exception x) { Index: xtest/nbjunit/test/unit/src/org/netbeans/junit/MockServicesTest.java =================================================================== RCS file: /shared/data/ccvs/repository/xtest/nbjunit/test/unit/src/org/netbeans/junit/MockServicesTest.java,v retrieving revision 1.3 diff -u -r1.3 MockServicesTest.java --- xtest/nbjunit/test/unit/src/org/netbeans/junit/MockServicesTest.java 12 May 2006 22:42:50 -0000 1.3 +++ xtest/nbjunit/test/unit/src/org/netbeans/junit/MockServicesTest.java 29 Jun 2006 15:40:02 -0000 @@ -21,6 +21,8 @@ import junit.framework.Test; import junit.framework.TestCase; import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; public abstract class MockServicesTest extends TestCase { @@ -40,6 +42,7 @@ } protected abstract Iterator lookup(Class clazz); + protected abstract void assertChangesFired(int countOfChanges); private String getChoice() { Iterator it = lookup(Choice.class); @@ -59,12 +62,16 @@ */ public void testGetChoice() { MockServices.setServices(); + assertChangesFired(1); assertEquals("initial value", "default", getChoice()); MockServices.setServices(MockChoice1.class); assertEquals("registered value", "mock1", getChoice()); + assertChangesFired(1); MockServices.setServices(MockChoice2.class); assertEquals("registered value", "mock2", getChoice()); + assertChangesFired(1); MockServices.setServices(MockChoice1.class, MockChoice2.class); + assertChangesFired(1); try { getChoice(); fail("Should not work on >1 choice"); @@ -223,16 +230,38 @@ } } - } + protected void assertChangesFired(int countOfChanges) { + // no changes listening supported + } - public static class LookupTest extends MockServicesTest { + } + public static class LookupTest extends MockServicesTest + implements LookupListener { + private Lookup.Result res; + private int cnt; + public LookupTest(String s) { super(s); + res = Lookup.getDefault().lookupResult(Object.class); + res.addLookupListener(this); + res.allInstances(); } protected Iterator lookup(Class clazz) { return Lookup.getDefault().lookupAll(clazz).iterator(); + } + + protected void assertChangesFired(int countOfChanges) { + if (countOfChanges <= cnt) { + cnt = 0; + } else { + fail("Not enough changes fired: " + cnt + " expected: " + countOfChanges); + } + } + + public void resultChanged(LookupEvent ev) { + cnt++; } } Index: core/startup/src/org/netbeans/core/startup/TopLogging.java =================================================================== RCS file: /shared/data/ccvs/repository/core/startup/src/org/netbeans/core/startup/TopLogging.java,v retrieving revision 1.15 diff -u -r1.15 TopLogging.java --- core/startup/src/org/netbeans/core/startup/TopLogging.java 29 Jun 2006 14:47:47 -0000 1.15 +++ core/startup/src/org/netbeans/core/startup/TopLogging.java 29 Jun 2006 15:40:07 -0000 @@ -23,6 +23,7 @@ import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.text.DateFormat; +import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -41,6 +42,8 @@ import java.util.logging.StreamHandler; import org.openide.filesystems.FileUtil; import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.xml.sax.SAXParseException; @@ -580,23 +583,39 @@ } } // end of LgStream - private static final class LookupDel extends Handler { + private static final class LookupDel extends Handler + implements LookupListener { + private Lookup.Result handlers; + private Collection instances; + + + public LookupDel() { + handlers = Lookup.getDefault().lookupResult(Handler.class); + instances = handlers.allInstances(); + handlers.addLookupListener(this); + } + + public void publish(LogRecord record) { - for (Handler h : Lookup.getDefault().lookupAll(Handler.class)) { + for (Handler h : instances) { h.publish(record); } } public void flush() { - for (Handler h : Lookup.getDefault().lookupAll(Handler.class)) { + for (Handler h : instances) { h.flush(); } } public void close() throws SecurityException { - for (Handler h : Lookup.getDefault().lookupAll(Handler.class)) { + for (Handler h : instances) { h.close(); } + } + + public void resultChanged(LookupEvent ev) { + instances = handlers.allInstances(); } } // end of LookupDel Index: core/startup/test/unit/src/org/netbeans/core/startup/TopLoggingLookupTest.java =================================================================== RCS file: /shared/data/ccvs/repository/core/startup/test/unit/src/org/netbeans/core/startup/TopLoggingLookupTest.java,v retrieving revision 1.2 diff -u -r1.2 TopLoggingLookupTest.java --- core/startup/test/unit/src/org/netbeans/core/startup/TopLoggingLookupTest.java 21 Jun 2006 06:40:22 -0000 1.2 +++ core/startup/test/unit/src/org/netbeans/core/startup/TopLoggingLookupTest.java 29 Jun 2006 15:40:07 -0000 @@ -34,6 +34,7 @@ import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; @@ -56,10 +57,6 @@ // initialize logging TopLogging.initialize(); - - MockServices.setServices(MyHandler.class); - handler = Lookup.getDefault().lookup(MyHandler.class); - assertNotNull("Handler found", handler); } @@ -67,13 +64,27 @@ } public void testLogOneLine() throws Exception { + MockServices.setServices(MyHandler.class); + handler = Lookup.getDefault().lookup(MyHandler.class); + assertNotNull("Handler found", handler); + + Logger.getLogger(TopLoggingTest.class.getName()).log(Level.INFO, "First visible message"); assertEquals("[First visible message]", handler.logs.toString()); + } + public void testDeadlock78865() throws Exception { + MockServices.setServices(AnotherThreadLoggingHandler.class); + handler = Lookup.getDefault().lookup(MyHandler.class); + assertNotNull("Handler found", handler); + + Logger.getLogger(TopLoggingTest.class.getName()).log(Level.INFO, "First visible message"); + + assertEquals("[First visible message]", handler.logs.toString()); } - public static final class MyHandler extends Handler { + public static class MyHandler extends Handler { public List logs = new ArrayList(); public void publish(LogRecord record) { @@ -86,6 +97,18 @@ public void close() throws SecurityException { logs.add("close"); + } + + } + public static final class AnotherThreadLoggingHandler extends MyHandler + implements Runnable { + public AnotherThreadLoggingHandler() { + Logger.global.info("in constructor before"); + RequestProcessor.getDefault().post(this).waitFinished(); + Logger.global.info("in constructor after"); + } + public void run() { + Logger.global.warning("running in parael"); } } Index: openide/util/arch.xml =================================================================== RCS file: /shared/data/ccvs/repository/openide/util/arch.xml,v retrieving revision 1.6 diff -u -r1.6 arch.xml --- openide/util/arch.xml 4 Apr 2006 15:06:01 -0000 1.6 +++ openide/util/arch.xml 29 Jun 2006 15:40:08 -0000 @@ -518,6 +518,11 @@ provided by org-openide-actions module when looking for org.openide.actions.ActionManager implementation. . + + There is a static private method Lookup.resetDefaultLookup that + is called by NbJUnit's MockServices to properly reset default + lookup and fire changes to all registred listeners. + .

Index: openide/util/src/org/openide/util/Lookup.java =================================================================== RCS file: /shared/data/ccvs/repository/openide/util/src/org/openide/util/Lookup.java,v retrieving revision 1.8 diff -u -r1.8 Lookup.java --- openide/util/src/org/openide/util/Lookup.java 2 Jun 2006 18:00:19 -0000 1.8 +++ openide/util/src/org/openide/util/Lookup.java 29 Jun 2006 15:40:08 -0000 @@ -13,6 +13,7 @@ package org.openide.util; +import com.sun.org.apache.bcel.internal.generic.LOOKUPSWITCH; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -118,13 +119,33 @@ return defaultLookup; } - // Had no such line, use simple impl. - // It does however need to have ClassLoader available or many things will break. - // Use the thread context classloader in effect now. - Lookup clLookup = Lookups.singleton(l); - defaultLookup = new ProxyLookup(new Lookup[] { misl, clLookup }); - - return defaultLookup; + DefLookup def = new DefLookup(); + def.init(l, misl); + return defaultLookup = def; + } + + private static final class DefLookup extends ProxyLookup { + public DefLookup() { + super(new Lookup[0]); + } + + public void init(ClassLoader loader, Lookup metaInfLookup) { + // Had no such line, use simple impl. + // It does however need to have ClassLoader available or many things will break. + // Use the thread context classloader in effect now. + Lookup clLookup = Lookups.singleton(loader); + setLookups(new Lookup[] { metaInfLookup, clLookup }); + } + } + + /** Called from MockServices to reset default lookup in case services change + */ + private static void resetDefaultLookup() { + if (defaultLookup instanceof DefLookup) { + DefLookup def = (DefLookup)defaultLookup; + ClassLoader l = Thread.currentThread().getContextClassLoader(); + def.init(l, Lookups.metaInfServices(l)); + } } /** Look up an object matching a given interface.