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.