openide.util/test/unit/src/org/openide/util/lookup/MetaInfServicesLookupTest.java
#160630: Making RecognizeInstanceObjectsTest pass by providing correct Thread.contextCL
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
30 * If you wish your version of this file to be governed by only the CDDL
31 * or only the GPL Version 2, indicate your decision by adding
32 * "[Contributor] elects to include this software in this distribution
33 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34 * single choice of license, a recipient has the option to distribute
35 * your version of this file under either the CDDL, the GPL Version 2 or
36 * to extend the choice of license to its licensees as provided above.
37 * However, if you add GPL Version 2 code and therefore, elected the GPL
38 * Version 2 license, then the option applies only if the new code is
39 * made subject to such option by the copyright holder.
42 package org.openide.util.lookup;
44 import java.awt.Component;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.lang.ref.Reference;
51 import java.lang.ref.WeakReference;
53 import java.net.URLClassLoader;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.Collections;
57 import java.util.Comparator;
58 import java.util.Enumeration;
59 import java.util.HashSet;
60 import java.util.Iterator;
61 import java.util.List;
64 import java.util.TreeSet;
65 import java.util.WeakHashMap;
66 import java.util.jar.JarEntry;
67 import java.util.jar.JarOutputStream;
68 import java.util.logging.Level;
69 import java.util.logging.Logger;
70 import java.util.regex.Matcher;
71 import java.util.regex.Pattern;
72 import junit.framework.Test;
73 import org.bar.Comparator2;
74 import org.netbeans.junit.MockServices;
75 import org.netbeans.junit.NbTestCase;
76 import org.openide.util.Enumerations;
77 import org.openide.util.Lookup;
78 import org.openide.util.LookupEvent;
79 import org.openide.util.LookupListener;
80 import org.openide.util.RequestProcessor;
81 import org.openide.util.test.MockLookup;
83 /** Test finding services from manifest.
86 public class MetaInfServicesLookupTest extends NbTestCase {
88 private Map<ClassLoader,Lookup> lookups = new WeakHashMap<ClassLoader,Lookup>();
90 public MetaInfServicesLookupTest(String name) {
92 LOG = Logger.getLogger("Test." + name);
95 protected String prefix() {
96 return "META-INF/services/";
99 protected Lookup createLookup(ClassLoader c) {
100 return Lookups.metaInfServices(c);
104 protected Level logLevel() {
108 private Lookup getTestedLookup(ClassLoader c) {
109 MockServices.setServices();
110 Lookup l = lookups.get(c);
118 private URL findJar(String n) throws IOException {
119 LOG.info("Looking for " + n);
120 File jarDir = new File(getWorkDir(), "jars");
122 File jar = new File(jarDir, n);
124 return jar.toURI().toURL();
127 LOG.info("generating " + jar);
129 URL data = MetaInfServicesLookupTest.class.getResource(n.replaceAll("\\.jar", "\\.txt"));
130 assertNotNull("Data found", data);
131 StringBuffer sb = new StringBuffer();
132 InputStreamReader r = new InputStreamReader(data.openStream());
141 JarOutputStream os = new JarOutputStream(new FileOutputStream(jar));
143 Pattern p = Pattern.compile(":([^:]+):([^:]*)", Pattern.MULTILINE | Pattern.DOTALL);
144 Matcher m = p.matcher(sb);
145 Pattern foobar = Pattern.compile("^(org\\.(foo|bar)\\..*)$", Pattern.MULTILINE);
146 Set<String> names = new TreeSet<String>();
148 assert m.groupCount() == 2;
149 String entryName = prefix() + m.group(1);
150 LOG.info("putting there entry: " + entryName);
151 os.putNextEntry(new JarEntry(entryName));
152 os.write(m.group(2).getBytes());
155 Matcher fb = foobar.matcher(m.group(2));
157 String clazz = fb.group(1).replace('.', '/') + ".class";
158 LOG.info("will copy " + clazz);
163 for (String copy : names) {
164 os.putNextEntry(new JarEntry(copy));
165 LOG.info("copying " + copy);
166 InputStream from = MetaInfServicesLookupTest.class.getResourceAsStream("/" + copy);
167 assertNotNull(copy, from);
169 int ch = from.read();
179 LOG.info("done " + jar);
180 return jar.toURI().toURL();
183 ClassLoader c1, c2, c2a, c3, c4;
186 protected void setUp() throws Exception {
188 ClassLoader app = getClass().getClassLoader().getParent();
189 ClassLoader c0 = app;
191 c1 = new URLClassLoader(new URL[] {
192 findJar("services-jar-1.jar"),
194 c2 = new URLClassLoader(new URL[] {
195 findJar("services-jar-2.jar"),
197 c2a = new URLClassLoader(new URL[] {
198 findJar("services-jar-2.jar"),
200 c3 = new URLClassLoader(new URL[] { findJar("services-jar-2.jar") },
203 c4 = new URLClassLoader(new URL[] {
204 findJar("services-jar-1.jar"),
205 findJar("services-jar-2.jar"),
210 protected void tearDown() throws Exception {
211 Set<Reference<Lookup>> weak = new HashSet<Reference<Lookup>>();
212 for (Lookup l : lookups.values()) {
213 weak.add(new WeakReference<Lookup>(l));
218 for(Reference<Lookup> ref : weak) {
219 assertGC("Lookup can disappear", ref);
223 public void testBasicUsage() throws Exception {
224 Lookup l = getTestedLookup(c2);
225 Class xface = c1.loadClass("org.foo.Interface");
226 List results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
227 assertEquals("Two items in result: " + results, 2, results.size());
228 // Note that they have to be in order:
229 assertEquals("org.foo.impl.Implementation1", results.get(0).getClass().getName());
230 assertEquals("org.bar.Implementation2", results.get(1).getClass().getName());
231 // Make sure it does not gratuitously replace items:
232 List results2 = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
233 assertEquals(results, results2);
236 public void testLoaderSkew() throws Exception {
237 Class xface1 = c1.loadClass("org.foo.Interface");
238 Lookup l3 = getTestedLookup(c3);
239 // If we cannot load Interface, there should be no impls of course... quietly!
240 assertEquals(Collections.EMPTY_LIST,
241 new ArrayList(l3.lookup(new Lookup.Template(xface1)).allInstances()));
242 Lookup l4 = getTestedLookup(c4);
243 // If we can load Interface but it is the wrong one, ignore it.
244 assertEquals(Collections.EMPTY_LIST,
245 new ArrayList(l4.lookup(new Lookup.Template(xface1)).allInstances()));
246 // Make sure l4 is really OK - it can load from its own JARs.
247 Class xface4 = c4.loadClass("org.foo.Interface");
248 assertEquals(2, l4.lookup(new Lookup.Template(xface4)).allInstances().size());
251 public void testStability() throws Exception {
252 Lookup l = getTestedLookup(c2);
253 Class xface = c1.loadClass("org.foo.Interface");
254 Object first = l.lookup(new Lookup.Template(xface)).allInstances().iterator().next();
255 l = getTestedLookup(c2a);
256 Object second = l.lookup(new Lookup.Template(xface)).allInstances().iterator().next();
257 assertEquals(first, second);
260 public void testMaskingOfResources() throws Exception {
261 Lookup l1 = getTestedLookup(c1);
262 Lookup l2 = getTestedLookup(c2);
263 Lookup l4 = getTestedLookup(c4);
265 assertNotNull("services1.jar defines a class that implements runnable", l1.lookup(Runnable.class));
266 assertNull("services2.jar does not defines a class that implements runnable", l2.lookup(Runnable.class));
267 assertNull("services1.jar defines Runnable, but services2.jar masks it out", l4.lookup(Runnable.class));
270 public void testOrdering() throws Exception {
271 Lookup l = getTestedLookup(c1);
272 Class xface = c1.loadClass("java.util.Comparator");
273 List results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
274 assertEquals(1, results.size());
276 l = getTestedLookup(c2);
277 xface = c2.loadClass("java.util.Comparator");
278 results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
279 assertEquals(2, results.size());
281 assertEquals("org.bar.Comparator2", results.get(0).getClass().getName());
282 assertEquals("org.foo.impl.Comparator1", results.get(1).getClass().getName());
284 // test that items without position are always at the end
285 l = getTestedLookup(c2);
286 xface = c2.loadClass("java.util.Iterator");
287 results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
288 assertEquals(2, results.size());
290 assertEquals("org.bar.Iterator2", results.get(0).getClass().getName());
291 assertEquals("org.foo.impl.Iterator1", results.get(1).getClass().getName());
294 public void testNoCallToGetResourceForObjectIssue65124() throws Exception {
295 class Loader extends ClassLoader {
299 protected URL findResource(String name) {
300 if (name.equals(prefix() + "java.lang.Object")) {
306 retValue = super.findResource(name);
311 protected Enumeration findResources(String name) throws IOException {
312 if (name.equals(prefix() + "java.lang.Object")) {
315 Enumeration retValue;
317 retValue = super.findResources(name);
321 Loader loader = new Loader();
322 Lookup l = getTestedLookup(loader);
324 Object no = l.lookup(String.class);
325 assertNull("Not found of course", no);
326 assertEquals("No lookup of Object", 0, loader.counter);
329 public void testCanGarbageCollectClasses() throws Exception {
330 class Loader extends ClassLoader {
332 super(Loader.class.getClassLoader().getParent());
336 protected URL findResource(String name) {
337 if (name.equals(prefix() + "java.lang.Runnable")) {
338 return Loader.class.getResource("MetaInfServicesLookupTestRunnable.txt");
343 retValue = super.findResource(name);
348 protected Class<?> findClass(String name) throws ClassNotFoundException {
349 if (name.equals("org.openide.util.lookup.MetaInfServicesLookupTestRunnable")) {
351 InputStream is = getClass().getResourceAsStream("MetaInfServicesLookupTestRunnable.class");
352 byte[] arr = new byte[is.available()];
353 int read = is.read(arr);
354 assertEquals("Fully read", arr.length, read);
355 return defineClass(name, arr, 0, arr.length);
356 } catch (IOException ex) {
357 throw new ClassNotFoundException("Cannot load", ex);
360 throw new ClassNotFoundException();
366 protected Enumeration findResources(String name) throws IOException {
367 if (name.equals(prefix() + "java.lang.Runnable")) {
368 return Enumerations.singleton(findResource(name));
370 return super.findResources(name);
373 Loader loader = new Loader();
374 Lookup l = getTestedLookup(loader);
377 Object no = l.lookup(Runnable.class);
378 assertNotNull("Found of course", no);
379 assertEquals("The right name", "MetaInfServicesLookupTestRunnable", no.getClass().getSimpleName());
380 if (no.getClass().getClassLoader() != loader) {
381 fail("Wrong classloader: " + no.getClass().getClassLoader());
384 WeakReference<Object> ref = new WeakReference<Object>(no.getClass());
389 MockLookup.setInstances();
390 Thread.currentThread().setContextClassLoader(null);
391 assertGC("Class can be garbage collected", ref);
394 public void testListenersAreNotifiedWithoutHoldingALockIssue36035() throws Exception {
395 final Lookup l = getTestedLookup(c2);
396 final Class xface = c1.loadClass("org.foo.Interface");
397 final Lookup.Result res = l.lookup(new Lookup.Template(Object.class));
399 class L implements LookupListener, Runnable {
400 private Thread toInterrupt;
403 assertNotNull("Possible to query lookup", l.lookup(xface));
404 assertEquals("and there are two items", 2, res.allInstances().size());
405 toInterrupt.interrupt();
408 public synchronized void resultChanged(LookupEvent ev) {
409 toInterrupt = Thread.currentThread();
410 RequestProcessor.getDefault().post(this);
413 fail("Should be interrupted - means it was not possible to finish query in run() method");
414 } catch (InterruptedException ex) {
415 // this is what we want
419 L listener = new L();
421 res.addLookupListener(listener);
422 assertEquals("Nothing yet", 0, res.allInstances().size());
424 assertNotNull("Interface found", l.lookup(xface));
425 assertNotNull("Listener notified", listener.toInterrupt);
427 assertEquals("Now two", 2, res.allInstances().size());
430 public void testWrongOrderAsInIssue100320() throws Exception {
431 ClassLoader app = getClass().getClassLoader().getParent();
432 ClassLoader c0 = app;
433 ClassLoader ctmp = new URLClassLoader(new URL[] {
434 findJar("problem100320.jar"),
436 Lookup lookup = Lookups.metaInfServices(ctmp, prefix());
438 Collection<?> colAWT = lookup.lookupAll(Component.class);
439 assertEquals("There is enough objects to switch to InheritanceTree", 12, colAWT.size());
442 List<?> col1 = new ArrayList<Object>(lookup.lookupAll(Comparator.class));
443 assertEquals("Two", 2, col1.size());
444 Collection<?> col2 = lookup.lookupAll(ctmp.loadClass(Comparator2.class.getName()));
445 assertEquals("One", 1, col2.size());
446 List<?> col3 = new ArrayList<Object>(lookup.lookupAll(Comparator.class));
447 assertEquals("Two2", 2, col3.size());
449 Iterator<?> it1 = col1.iterator();
450 Iterator<?> it3 = col3.iterator();
452 it1.next() != it3.next() ||
453 it1.next() != it3.next()
455 fail("Collections are different:\nFirst: " + col1 + "\nLast: " + col3);