cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject [01/10] cayenne git commit: CAY-2166 Auto-loading of Cayenne modules
Date Sun, 11 Dec 2016 17:59:55 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master e2624ed77 -> 2bc717d28


CAY-2166 Auto-loading of Cayenne modules


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/2fd200b0
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/2fd200b0
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/2fd200b0

Branch: refs/heads/master
Commit: 2fd200b03196cf490c94acfddf3792e8d466c52e
Parents: e2624ed
Author: Andrus Adamchik <andrus@objectstyle.com>
Authored: Sun Dec 11 17:05:33 2016 +0300
Committer: Andrus Adamchik <andrus@objectstyle.com>
Committed: Sun Dec 11 17:39:01 2016 +0300

----------------------------------------------------------------------
 .../java/org/apache/cayenne/di/spi/DIGraph.java |  21 ++-
 .../org/apache/cayenne/di/spi/ListProvider.java |   3 -
 .../org/apache/cayenne/di/spi/ModuleLoader.java |  87 +++++++++++
 .../apache/cayenne/di/spi/ModuleProvider.java   |  41 +++++
 .../org/apache/cayenne/di/spi/DIGraphTest.java  |  58 +++++++
 .../apache/cayenne/di/spi/ModuleLoaderTest.java | 152 +++++++++++++++++++
 .../org.apache.cayenne.di.spi.ModuleProvider    |   4 +
 7 files changed, 351 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIGraph.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIGraph.java b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIGraph.java
index 5de1cd8..6e8592b 100644
--- a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIGraph.java
+++ b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/DIGraph.java
@@ -19,14 +19,9 @@
 
 package org.apache.cayenne.di.spi;
 
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import org.apache.cayenne.di.DIRuntimeException;
+
+import java.util.*;
 
 /**
  * The implementation here is basically an adjacency list, but a {@link Map} is
@@ -39,7 +34,7 @@ import java.util.Map;
 class DIGraph<V> {
 
 	/**
-	 * Note: {@link LinkedHashMap} is used for supporting insertion order.
+	 * {@link LinkedHashMap} is used for supporting insertion order.
 	 */
 	private Map<V, List<V>> neighbors = new LinkedHashMap<>();
 
@@ -129,8 +124,7 @@ class DIGraph<V> {
 	}
 
 	/**
-	 * Return (as a List) the topological sort of the vertices; null for no such
-	 * sort.
+	 * Return (as a List) the topological sort of the vertices. Throws an exception if cycles
are detected.
 	 */
 	public List<V> topSort() {
 		Map<V, Integer> degree = inDegree();
@@ -157,7 +151,7 @@ class DIGraph<V> {
 
 		// Check that we have used the entire graph (if not, there was a cycle)
 		if (result.size() != neighbors.size()) {
-			return null;
+			throw new DIRuntimeException("Dependency cycle detected in DI container");
 		}
 
 		return result;
@@ -176,4 +170,7 @@ class DIGraph<V> {
 		return s.toString();
 	}
 
+	public int size() {
+		return neighbors.size();
+	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ListProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ListProvider.java b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ListProvider.java
index 2178635..9fee307 100644
--- a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ListProvider.java
+++ b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ListProvider.java
@@ -49,9 +49,6 @@ class ListProvider implements Provider<List<?>> {
     public List<?> get() throws DIRuntimeException {
         List<Key<?>> insertOrder = graph.topSort();
 
-        if (insertOrder == null)
-            throw new DIRuntimeException("Dependency cycle detected in DI container");
-
         if (insertOrder.size() != providers.size()) {
             List<Key<?>> emptyKeys = new ArrayList<>();
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleLoader.java b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleLoader.java
new file mode 100644
index 0000000..8ea771e
--- /dev/null
+++ b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleLoader.java
@@ -0,0 +1,87 @@
+/*
+ *    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.apache.cayenne.di.spi;
+
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Module;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+/**
+ * Auto-loads DI modules using ServiceLoader. To make a module auto-loadable, you will need
to ship the jar with a file
+ * "META-INF/services/org.apache.cayenne.di.spi.ModuleProvider" that contains provider implementation
for the module in
+ * question.
+ *
+ * @since 4.0
+ */
+public class ModuleLoader {
+
+    /**
+     * Auto-loads all modules declared on classpath. Modules are loaded from the SPI declarations
stored in
+     * "META-INF/services/org.apache.cayenne.di.spi.ModuleProvider", and then sorted in the
order of override dependency.
+     *
+     * @return a sorted collection of auto-loadable modules.
+     * @throws DIRuntimeException if auto-loaded modules have circular override dependencies.
+     */
+    public List<Module> load() {
+
+        // map providers by class
+
+        Map<Class<? extends Module>, ModuleProvider> providers = new HashMap<>();
+
+        for (ModuleProvider provider : ServiceLoader.load(ModuleProvider.class)) {
+
+            ModuleProvider existing = providers.put(provider.moduleType(), provider);
+            if (existing != null && !existing.getClass().equals(provider.getClass()))
{
+                throw new DIRuntimeException("More than one provider for module type '%s':
%s and %s",
+                        provider.moduleType().getName(),
+                        existing.getClass().getName(),
+                        provider.getClass().getName());
+            }
+        }
+
+        if (providers.size() == 0) {
+            return Collections.emptyList();
+        }
+
+        // do override dependency sort...
+        DIGraph<Class<? extends Module>> overrideGraph = new DIGraph<>();
+        for (Map.Entry<Class<? extends Module>, ModuleProvider> e : providers.entrySet())
{
+
+            overrideGraph.add(e.getKey());
+            for (Class<? extends Module> overridden : e.getValue().overrides()) {
+                overrideGraph.add(e.getKey(), overridden);
+            }
+        }
+
+        Collection<Class<? extends Module>> moduleTypes = overrideGraph.topSort();
+        List<Module> modules = new ArrayList<>(moduleTypes.size());
+        for (Class<? extends Module> type : moduleTypes) {
+            modules.add(providers.get(type).module());
+        }
+
+        return modules;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleProvider.java b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleProvider.java
new file mode 100644
index 0000000..e96322e
--- /dev/null
+++ b/cayenne-di/src/main/java/org/apache/cayenne/di/spi/ModuleProvider.java
@@ -0,0 +1,41 @@
+/*
+ *    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.apache.cayenne.di.spi;
+
+import org.apache.cayenne.di.Module;
+
+/**
+ * Provider of modules used by module auto-loading mechanism to identify and load modules.
+ *
+ * @since 4.0
+ */
+public interface ModuleProvider {
+
+    Module module();
+
+    Class<? extends Module> moduleType();
+
+    /**
+     * Returns an array of module types this module overrides. Module auto-loading mechanism
will ensure module
+     * load order that respects overriding preferences.
+     *
+     * @return a collection of module types this module overrides.
+     */
+    Class<? extends Module>[] overrides();
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DIGraphTest.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DIGraphTest.java b/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DIGraphTest.java
new file mode 100644
index 0000000..dcfe1e8
--- /dev/null
+++ b/cayenne-di/src/test/java/org/apache/cayenne/di/spi/DIGraphTest.java
@@ -0,0 +1,58 @@
+/*
+ *    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.apache.cayenne.di.spi;
+
+import org.apache.cayenne.di.DIRuntimeException;
+import org.junit.Test;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+
+public class DIGraphTest {
+
+    @Test
+    public void testTopSortNoCycles() {
+        DIGraph<String> graph = new DIGraph<>();
+        graph.add("x", "y");
+        graph.add("x", "z");
+        graph.add("z", "a");
+
+        List<String> sorted = graph.topSort();
+        assertEquals(asList("y", "a", "z", "x"), sorted);
+    }
+
+    @Test(expected = DIRuntimeException.class)
+    public void testTopSortDirectCycle() {
+        DIGraph<String> graph = new DIGraph<>();
+        graph.add("x", "y");
+        graph.add("y", "x");
+        graph.topSort();
+    }
+
+    @Test(expected = DIRuntimeException.class)
+    public void testTopSortInDirectCycle() {
+        DIGraph<String> graph = new DIGraph<>();
+        graph.add("x", "y");
+        graph.add("y", "z");
+        graph.add("z", "x");
+        graph.topSort();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/test/java/org/apache/cayenne/di/spi/ModuleLoaderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-di/src/test/java/org/apache/cayenne/di/spi/ModuleLoaderTest.java b/cayenne-di/src/test/java/org/apache/cayenne/di/spi/ModuleLoaderTest.java
new file mode 100644
index 0000000..3456b5b
--- /dev/null
+++ b/cayenne-di/src/test/java/org/apache/cayenne/di/spi/ModuleLoaderTest.java
@@ -0,0 +1,152 @@
+/*
+ *    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.apache.cayenne.di.spi;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ModuleLoaderTest {
+
+    @Test
+    public void testLoad() {
+
+        List<Module> modules = new ModuleLoader().load();
+        assertEquals(4, modules.size());
+        assertTrue(modules.get(0) instanceof Module2);
+        assertTrue(modules.get(1) instanceof Module1);
+        assertTrue(modules.get(2) instanceof Module3);
+        assertTrue(modules.get(3) instanceof Module4);
+
+        Injector i = DIBootstrap.createInjector(modules);
+        assertEquals("a", i.getInstance(String.class));
+        assertEquals(Integer.valueOf(56), i.getInstance(Integer.class));
+    }
+
+    public static class Module1 implements Module {
+
+        @Override
+        public void configure(Binder binder) {
+            binder.bind(String.class).toInstance("a");
+        }
+    }
+
+    public static class Module2 implements Module {
+
+        @Override
+        public void configure(Binder binder) {
+            binder.bind(String.class).toInstance("b");
+        }
+    }
+
+    public static class Module3 implements Module {
+
+        @Override
+        public void configure(Binder binder) {
+            binder.bind(Integer.class).toInstance(66);
+        }
+    }
+
+    public static class Module4 implements Module {
+
+        @Override
+        public void configure(Binder binder) {
+            binder.bind(Integer.class).toInstance(56);
+        }
+    }
+
+    public static class ModuleProvider1 implements ModuleProvider {
+
+        @Override
+        public Module module() {
+            return new Module1();
+        }
+
+        @Override
+        public Class<? extends Module> moduleType() {
+            return Module1.class;
+        }
+
+        @Override
+        public Class<? extends Module>[] overrides() {
+            return new Class[]{Module2.class};
+        }
+    }
+
+    public static class ModuleProvider2 implements ModuleProvider {
+
+        @Override
+        public Module module() {
+            return new Module2();
+        }
+
+        @Override
+        public Class<? extends Module> moduleType() {
+            return Module2.class;
+        }
+
+        @Override
+        public Class<? extends Module>[] overrides() {
+            return new Class[0];
+        }
+    }
+
+    public static class ModuleProvider3 implements ModuleProvider {
+
+        @Override
+        public Module module() {
+            return new Module3();
+        }
+
+        @Override
+        public Class<? extends Module> moduleType() {
+            return Module3.class;
+        }
+
+        @Override
+        public Class<? extends Module>[] overrides() {
+            return new Class[0];
+        }
+    }
+
+    public static class ModuleProvider4 implements ModuleProvider {
+
+        @Override
+        public Module module() {
+            return new Module4();
+        }
+
+        @Override
+        public Class<? extends Module> moduleType() {
+            return Module4.class;
+        }
+
+        @Override
+        public Class<? extends Module>[] overrides() {
+            return new Class[]{Module3.class};
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2fd200b0/cayenne-di/src/test/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
----------------------------------------------------------------------
diff --git a/cayenne-di/src/test/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
b/cayenne-di/src/test/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
new file mode 100644
index 0000000..ff2d444
--- /dev/null
+++ b/cayenne-di/src/test/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
@@ -0,0 +1,4 @@
+org.apache.cayenne.di.spi.ModuleLoaderTest$ModuleProvider1
+org.apache.cayenne.di.spi.ModuleLoaderTest$ModuleProvider2
+org.apache.cayenne.di.spi.ModuleLoaderTest$ModuleProvider3
+org.apache.cayenne.di.spi.ModuleLoaderTest$ModuleProvider4
\ No newline at end of file


Mime
View raw message