diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/util/service/ServiceRegistry.java b/common/common-lang/src/main/java/com/twelvemonkeys/util/service/ServiceRegistry.java index b271336b..c375d955 100755 --- a/common/common-lang/src/main/java/com/twelvemonkeys/util/service/ServiceRegistry.java +++ b/common/common-lang/src/main/java/com/twelvemonkeys/util/service/ServiceRegistry.java @@ -59,7 +59,7 @@ import java.util.*; * * * @author Harald Kuhr - * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/ServiceRegistry.java#2 $ + * @version $Id: com/twelvemonkeys/util/service/ServiceRegistry.java#2 $ * @see RegisterableService * @see JAR File Specification */ @@ -110,7 +110,7 @@ public class ServiceRegistry { * Registers all provider implementations for this {@code ServiceRegistry} * found in the application classpath. * - * @throws ServiceConfigurationError if an error occured during registration + * @throws ServiceConfigurationError if an error occurred during registration */ public void registerApplicationClasspathSPIs() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); @@ -140,7 +140,7 @@ public class ServiceRegistry { * * @param pResource the resource to load SPIs from * @param pCategory the category class - * @param pLoader the classloader to use + * @param pLoader the class loader to use */ void registerSPIs(final URL pResource, final Class pCategory, final ClassLoader pLoader) { Properties classNames = new Properties(); @@ -242,7 +242,7 @@ public class ServiceRegistry { * The iterator supports removal. *

* - * NOTE: Removing a category from the iterator, deregisters + * NOTE: Removing a category from the iterator, de-registers * {@code pProvider} from the current category (as returned by the last * invocation of {@code next()}), it does not remove the category * itself from the registry. @@ -257,7 +257,7 @@ public class ServiceRegistry { return new FilterIterator>(categories(), new FilterIterator.Filter>() { public boolean accept(Class pElement) { - return getRegistry(pElement).contatins(pProvider); + return getRegistry(pElement).contains(pProvider); } }) { Class current; @@ -297,7 +297,7 @@ public class ServiceRegistry { * * @param pProvider the provider instance * @return {@code true} if {@code pProvider} is now registered in - * one or more categories + * one or more categories it was not registered in before. * @see #compatibleCategories(Object) */ public boolean register(final Object pProvider) { @@ -329,12 +329,12 @@ public class ServiceRegistry { } /** - * Deregisters the given provider from all categories it's currently + * De-registers the given provider from all categories it's currently * registered in. * * @param pProvider the provider instance * @return {@code true} if {@code pProvider} was previously registered in - * any category + * any category and is now de-registered. * @see #containingCategories(Object) */ public boolean deregister(final Object pProvider) { @@ -384,9 +384,8 @@ public class ServiceRegistry { public boolean register(final T pProvider) { checkCategory(pProvider); - // NOTE: We only register the new instance, if we don't allready - // have an instance of pProvider's class. - if (!contatins(pProvider)) { + // NOTE: We only register the new instance, if we don't already have an instance of pProvider's class. + if (!contains(pProvider)) { providers.put(pProvider.getClass(), pProvider); processRegistration(pProvider); return true; @@ -424,8 +423,8 @@ public class ServiceRegistry { } } - public boolean contatins(final Object pProvider) { - return providers.containsKey(pProvider.getClass()); + public boolean contains(final Object pProvider) { + return providers.containsKey(pProvider != null ? pProvider.getClass() : null); } public Iterator providers() { diff --git a/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPI.java b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPI.java new file mode 100644 index 00000000..aa078a3e --- /dev/null +++ b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPI.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util.service; + +/** +* ExampleSPI +* +* @author Harald Kuhr +* @author last modified by $Author: haraldk$ +* @version $Id: ExampleSPI.java,v 1.0 25.01.12 16:24 haraldk Exp$ +*/ +abstract class DummySPI { +} diff --git a/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIImpl.java b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIImpl.java new file mode 100644 index 00000000..8ae8b908 --- /dev/null +++ b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util.service; + +/** + * DummySPIImpl + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: DummySPIImpl.java,v 1.0 25.01.12 16:25 haraldk Exp$ + */ +class DummySPIImpl extends DummySPI { +} diff --git a/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIToo.java b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIToo.java new file mode 100644 index 00000000..4d2f6c73 --- /dev/null +++ b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/DummySPIToo.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util.service; + +/** + * DummySPIToo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: DummySPIToo.java,v 1.0 25.01.12 16:27 haraldk Exp$ + */ +class DummySPIToo extends DummySPI { +} diff --git a/common/common-lang/src/test/java/com/twelvemonkeys/util/service/ServiceRegistryTest.java b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/ServiceRegistryTest.java new file mode 100644 index 00000000..fe7b8f32 --- /dev/null +++ b/common/common-lang/src/test/java/com/twelvemonkeys/util/service/ServiceRegistryTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2012, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util.service; + +import com.twelvemonkeys.util.CollectionUtil; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +/** + * ServiceRegistryTest + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ServiceRegistryTest.java,v 1.0 25.01.12 16:16 haraldk Exp$ + */ +public class ServiceRegistryTest { + + private final TestRegistry registry = new TestRegistry(); + + @Test(expected = IllegalArgumentException.class) + public void testCreateNull() { + new ServiceRegistry(null); + } + + @Test + public void testCreateEmptyIterator() { + // A completely useless registry... + ServiceRegistry registry = new ServiceRegistry(Collections.>emptyList().iterator()); + registry.registerApplicationClasspathSPIs(); + + while (registry.categories().hasNext()) { + fail("No categories"); + } + } + + @Test(expected = ServiceConfigurationError.class) + public void testCreateBadConfig() { + @SuppressWarnings("unchecked") + ServiceRegistry registry = new ServiceRegistry(Arrays.asList(BadSPI.class).iterator()); + registry.registerApplicationClasspathSPIs(); + + // DONE: Test non-class + + // TODO: Test class not implementing SPI category + // TODO: Test class that throws exception in constructor + // TODO: Test class that has no public no-args constructor + // TODO: Test IOException + // Some of these can be tested using stubs, via the package protected registerSPIs method + } + + @Test + public void testCategories() { + // Categories + Iterator> categories = registry.categories(); + assertTrue(categories.hasNext()); + Class category = categories.next(); + assertEquals(DummySPI.class, category); + assertFalse(categories.hasNext()); + } + + @Test + public void testProviders() { + // Providers + Iterator providers = registry.providers(DummySPI.class); + List providerList = new ArrayList(); + CollectionUtil.addAll(providerList, providers); + + assertEquals(2, providerList.size()); + + // Order should be as in configuration file + assertNotNull(providerList.get(0)); + assertEquals(DummySPIImpl.class, providerList.get(0).getClass()); + assertNotNull(providerList.get(1)); + assertEquals(DummySPIToo.class, providerList.get(1).getClass()); + } + + @Test + public void testCompatibleCategoriesNull() { + // Compatible categories + Iterator> categories = registry.compatibleCategories(null); + assertFalse(categories.hasNext()); + } + + @Test + public void testCompatibleCategoriesImpl() { + Iterator> categories = registry.compatibleCategories(new DummySPIImpl()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testCompatibleCategoriesToo() { + Iterator> categories = registry.compatibleCategories(new DummySPIToo()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testCompatibleCategoriesNonRegistered() { + Iterator> categories = registry.compatibleCategories(new DummySPI() {}); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testCompatibleCategoriesUnknownType() { + Iterator> categories = registry.compatibleCategories(new Object()); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesNull() { + // Containing categories + Iterator> categories = registry.containingCategories(null); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesKnownInstanceImpl() { + Iterator providers = registry.providers(DummySPI.class); + assertTrue(providers.hasNext()); // Sanity check + + Iterator> categories = registry.containingCategories(providers.next()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesKnownInstanceToo() { + Iterator providers = registry.providers(DummySPI.class); + providers.next(); + assertTrue(providers.hasNext()); // Sanity check + + Iterator> categories = registry.containingCategories(providers.next()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesNewInstanceRegisteredImpl() { + // NOTE: Currently we match based on type, rather than instance, but it does make sense... + Iterator> categories = registry.containingCategories(new DummySPIImpl()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesNewInstanceRegisteredToo() { + // NOTE: Currently we match based on type, rather than instance, but it does make sense... + Iterator> categories = registry.containingCategories(new DummySPIToo()); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesCompatibleNonRegisteredType() { + Iterator> categories = registry.containingCategories(new DummySPI() {}); + assertFalse(categories.hasNext()); + } + + @Test + public void testContainingCategoriesUnknownType() { + Iterator> categories = registry.containingCategories(new Object()); + assertFalse(categories.hasNext()); + } + + @Test + public void testRegister() { + // Register + DummySPI dummy = new DummySPI() {}; + assertTrue(registry.register(dummy)); + + // Should now have category + Iterator> categories = registry.containingCategories(dummy); + assertTrue(categories.hasNext()); + assertEquals(DummySPI.class, categories.next()); + assertFalse(categories.hasNext()); + + // Should now be in providers + Iterator providers = registry.providers(DummySPI.class); + List providerList = new ArrayList(); + CollectionUtil.addAll(providerList, providers); + + assertEquals(3, providerList.size()); + + assertNotNull(providerList.get(1)); + assertSame(dummy, providerList.get(2)); + } + + @Test + public void testRegisterAlreadyRegistered() { + Iterator providers = registry.providers(DummySPI.class); + assertTrue(providers.hasNext()); // Sanity check + + assertFalse(registry.register(providers.next())); + } + + @Test + public void testRegisterNull() { + assertFalse(registry.register(null)); + } + + @Test + public void testRegisterIncompatible() { + assertFalse(registry.register(new Object())); + } + + @Test + public void testDeregisterNull() { + assertFalse(registry.deregister(null)); + } + + @Test + public void testDeregisterIncompatible() { + assertFalse(registry.deregister(new Object())); + } + + @Test + public void testDeregisterCompatibleNonRegistered() { + DummySPI dummy = new DummySPI() {}; + assertFalse(registry.deregister(dummy)); + } + + @Test + public void testDeregister() { + Iterator providers = registry.providers(DummySPI.class); + assertTrue(providers.hasNext()); // Sanity check + DummySPI instance = providers.next(); + assertTrue(registry.deregister(instance)); + + // Test no longer in registry + providers = registry.providers(DummySPI.class); + int count = 0; + while (providers.hasNext()) { + DummySPI next = providers.next(); + assertNotSame(instance, next); + count++; + } + + assertEquals(1, count); + } + + // TODO: Test register with category + // TODO: Test register with unknown category + // TODO: Test register with null category + + // TODO: Test de-register with category + // TODO: Test de-register with unknown category + // TODO: Test de-register with null category + + + private static class TestRegistry extends ServiceRegistry { + @SuppressWarnings("unchecked") + public TestRegistry() { + super(Arrays.asList(DummySPI.class).iterator()); + registerApplicationClasspathSPIs(); + } + } + + public static class BadSPI {} +} diff --git a/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.DummySPI b/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.DummySPI new file mode 100644 index 00000000..fc5fac87 --- /dev/null +++ b/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.DummySPI @@ -0,0 +1,2 @@ +com.twelvemonkeys.util.service.DummySPIImpl +com.twelvemonkeys.util.service.DummySPIToo diff --git a/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.ServiceRegistryTest$BadSPI b/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.ServiceRegistryTest$BadSPI new file mode 100644 index 00000000..39afdee7 --- /dev/null +++ b/common/common-lang/src/test/resources/META-INF/services/com.twelvemonkeys.util.service.ServiceRegistryTest$BadSPI @@ -0,0 +1,3 @@ +# Any line that does not resolve to a class name, will make registerApplicationClasspathSPIs throw error +Bad, bad spi +So bad. So bad.