You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2016/12/11 17:59:56 UTC

[02/10] cayenne git commit: CAY-2166 Auto-loading of Cayenne modules

CAY-2166 Auto-loading of Cayenne modules

* refactoring modules and runtimes to prepare for auto-loading...
  essentially making things more flexible and removing self-init
  code into builders


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

Branch: refs/heads/master
Commit: 3dc06bd78c39509fbab4a88791507d0ec66921a6
Parents: 2fd200b
Author: Andrus Adamchik <an...@objectstyle.com>
Authored: Sun Dec 11 17:05:33 2016 +0300
Committer: Andrus Adamchik <an...@objectstyle.com>
Committed: Sun Dec 11 18:40:28 2016 +0300

----------------------------------------------------------------------
 .../rop/client/ClientLocalRuntime.java          |  59 +++--
 .../configuration/rop/client/ClientRuntime.java |  40 ++-
 .../rop/client/ClientLocalRuntimeTest.java      |   3 +-
 .../rop/client/ClientRuntimeTest.java           |   7 +-
 .../cayenne/crypto/Runtime_AES128_Base.java     |   2 +-
 .../value/DefaultValueTransformerFactoryIT.java |   2 +-
 .../cayenne/configuration/CayenneRuntime.java   | 242 +++++++++---------
 .../cayenne/configuration/ModuleCollection.java |  82 ------
 .../configuration/server/ServerModule.java      |  26 +-
 .../configuration/server/ServerRuntime.java     |  53 +++-
 .../server/ServerRuntimeBuilder.java            |  63 +++--
 .../rop/server/ROPServletTest.java              |  43 ++--
 .../server/ServerRuntimeBuilderTest.java        |  33 ++-
 .../configuration/server/ServerRuntimeTest.java | 247 +++++++++----------
 .../configuration/web/CayenneFilterTest.java    |  19 +-
 15 files changed, 473 insertions(+), 448 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntime.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntime.java b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntime.java
index 81a731d..2e11a0d 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntime.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntime.java
@@ -18,44 +18,61 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.rop.client;
 
-import java.util.Collection;
-import java.util.Map;
-
 import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Key;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.remote.ClientConnection;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
 /**
  * A {@link ClientRuntime} that provides an ROP stack based on a local
  * connection on top of a server stack.
- * 
+ *
  * @since 3.1
  */
+// TODO: module auto-loading and ClientLocalRuntimeBuilder
 public class ClientLocalRuntime extends ClientRuntime {
 
-	public static final String CLIENT_SERVER_CHANNEL_KEY = "client-server-channel";
+    public static final String CLIENT_SERVER_CHANNEL_KEY = "client-server-channel";
+
+    private static Collection<Module> collectModules(
+            Injector serverInjector,
+            Module... extraModules) {
+
+        Collection<Module> modules = extraModules != null ? asList(extraModules) : Collections.<Module>emptyList();
+        return collectModules(serverInjector, modules);
+    }
+
+    private static Collection<Module> collectModules(final Injector serverInjector, Collection<Module> extraModules) {
+
+        Collection<Module> modules = new ArrayList<>(extraModules.size() + 1);
+        modules.add(new Module() {
 
-	private static ModuleCollection mainModuleOverride(final Injector serverInjector) {
-		return new ModuleCollection(new Module() {
+            public void configure(Binder binder) {
+                binder.bind(Key.get(DataChannel.class, CLIENT_SERVER_CHANNEL_KEY)).toProviderInstance(
+                        new LocalClientServerChannelProvider(serverInjector));
+                binder.bind(ClientConnection.class).toProviderInstance(new LocalConnectionProvider());
+            }
+        });
 
-			public void configure(Binder binder) {
-				binder.bind(Key.get(DataChannel.class, CLIENT_SERVER_CHANNEL_KEY)).toProviderInstance(
-						new LocalClientServerChannelProvider(serverInjector));
-				binder.bind(ClientConnection.class).toProviderInstance(new LocalConnectionProvider());
-			}
-		});
-	}
+        modules.addAll(extraModules);
+        return modules;
+    }
 
-	public ClientLocalRuntime(Injector serverInjector, Map<String, String> properties, Collection<Module> extraModules) {
-		super(properties, mainModuleOverride(serverInjector).add(extraModules));
-	}
+    public ClientLocalRuntime(Injector serverInjector, Map<String, String> properties, Collection<Module> extraModules) {
+        super(properties, collectModules(serverInjector, extraModules));
+    }
 
-	public ClientLocalRuntime(Injector serverInjector, Map<String, String> properties, Module... extraModules) {
-		super(properties, mainModuleOverride(serverInjector).add(extraModules));
-	}
+    public ClientLocalRuntime(Injector serverInjector, Map<String, String> properties, Module... extraModules) {
+        super(properties, collectModules(serverInjector, extraModules));
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientRuntime.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientRuntime.java b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientRuntime.java
index c0ddc05..7e54c8c 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientRuntime.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientRuntime.java
@@ -18,23 +18,40 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.rop.client;
 
-import java.util.Collection;
-import java.util.Map;
-
 import org.apache.cayenne.configuration.CayenneRuntime;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.remote.ClientConnection;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
 /**
  * A user application entry point to Cayenne stack on the ROP client.
  * 
  * @since 3.1
  */
+// TODO: module auto-loading and ClientRuntimeBuilder
 public class ClientRuntime extends CayenneRuntime {
 
-	private static ModuleCollection mainModule(Map<String, String> properties) {
-		return new ModuleCollection(new ClientModule(properties));
+	private static Collection<Module> collectModules(Map<String, String> properties, Module... extraModules) {
+
+		Collection<Module> modules = extraModules != null ? asList(extraModules) : Collections.<Module>emptyList();
+		return collectModules(properties, modules);
+	}
+
+	private static Collection<Module> collectModules(Map<String, String> properties, Collection<Module> extraModules) {
+		Collection<Module> modules = new ArrayList<>();
+		modules.add(new ClientModule(properties));
+
+		if(extraModules != null) {
+			modules.addAll(extraModules);
+		}
+
+		return modules;
 	}
 
 	/**
@@ -46,7 +63,7 @@ public class ClientRuntime extends CayenneRuntime {
 	 * services.
 	 */
 	public ClientRuntime(Map<String, String> properties, Collection<Module> extraModules) {
-		super(mainModule(properties).add(extraModules));
+		this(collectModules(properties, extraModules));
 	}
 
 	/**
@@ -58,7 +75,14 @@ public class ClientRuntime extends CayenneRuntime {
 	 * user services.
 	 */
 	public ClientRuntime(Map<String, String> properties, Module... extraModules) {
-		super(mainModule(properties).add(extraModules));
+		this(collectModules(properties, extraModules));
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	protected ClientRuntime(Collection<Module> modules) {
+		super(modules);
 	}
 
 	public ClientConnection getConnection() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntimeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntimeTest.java b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntimeTest.java
index b49e1db..d581104 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntimeTest.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientLocalRuntimeTest.java
@@ -22,7 +22,6 @@ import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.access.ClientServerChannel;
 import org.apache.cayenne.access.DataContext;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.configuration.ObjectContextFactory;
 import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.DIBootstrap;
@@ -53,7 +52,7 @@ public class ClientLocalRuntimeTest {
 
 		ClientLocalRuntime runtime = new ClientLocalRuntime(DIBootstrap.createInjector(serverModule),
 				Collections.<String, String> emptyMap());
-		Collection<Module> cmodules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> cmodules = runtime.getModules();
 		assertEquals(2, cmodules.size());
 
 		assertTrue(cmodules.toArray()[0] instanceof ClientModule);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientRuntimeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientRuntimeTest.java b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientRuntimeTest.java
index 86fea70..2156299 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientRuntimeTest.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientRuntimeTest.java
@@ -21,7 +21,6 @@ package org.apache.cayenne.configuration.rop.client;
 import org.apache.cayenne.CayenneContext;
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.event.DefaultEventManager;
@@ -50,7 +49,7 @@ public class ClientRuntimeTest {
 	public void testDefaultConstructor() {
 		ClientRuntime runtime = new ClientRuntime(Collections.<String, String> emptyMap());
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> modules = runtime.getModules();
 		assertEquals(1, modules.size());
 		Object[] marray = modules.toArray();
 
@@ -79,7 +78,7 @@ public class ClientRuntimeTest {
 		Map<String, String> properties = new HashMap<>();
 
 		ClientRuntime runtime = new ClientRuntime(properties, m1, m2);
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> modules = runtime.getModules();
 		assertEquals(3, modules.size());
 
 		assertTrue(configured[0]);
@@ -110,7 +109,7 @@ public class ClientRuntimeTest {
 		Map<String, String> properties = new HashMap<>();
 
 		ClientRuntime runtime = new ClientRuntime(properties, modules);
-		Collection<Module> cmodules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> cmodules = runtime.getModules();
 		assertEquals(3, cmodules.size());
 
 		assertTrue(configured[0]);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
index 4b5774c..2d11f86 100644
--- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
+++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
@@ -56,7 +56,7 @@ public class Runtime_AES128_Base {
     }
 
     protected ServerRuntime createRuntime(Module crypto) {
-        return new ServerRuntime("cayenne-crypto.xml", crypto);
+        return ServerRuntime.builder().addConfig("cayenne-crypto.xml").addModule(crypto).build();
     }
 
     protected Module createCryptoModule(boolean compress) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactoryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactoryIT.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactoryIT.java
index e40137e..9f42758 100644
--- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactoryIT.java
+++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactoryIT.java
@@ -52,7 +52,7 @@ public class DefaultValueTransformerFactoryIT {
 
     @BeforeClass
     public static void beforeClass() throws Exception {
-        ServerRuntime runtime = new ServerRuntime("cayenne-crypto.xml");
+        ServerRuntime runtime = ServerRuntime.builder().addConfig("cayenne-crypto.xml").build();
         t1 = runtime.getChannel().getEntityResolver().getDbEntity("TABLE1");
         t2 = runtime.getChannel().getEntityResolver().getDbEntity("TABLE2");
         t3 = runtime.getChannel().getEntityResolver().getDbEntity("TABLE3");

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/main/java/org/apache/cayenne/configuration/CayenneRuntime.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/CayenneRuntime.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/CayenneRuntime.java
index 9ee5379..822a1b0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/CayenneRuntime.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/CayenneRuntime.java
@@ -25,140 +25,130 @@ import org.apache.cayenne.di.DIBootstrap;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Module;
 
+import java.util.Collection;
+import java.util.Objects;
+
 /**
  * A superclass of various Cayenne runtime stacks. A Runtime is the main access
  * point to Cayenne for a user application. It provides a default Cayenne
  * configuration as well as a way to customize this configuration via a built-in
  * dependency injection (DI) container. In fact implementation-wise, Runtime
  * object is just a convenience thin wrapper around a DI {@link Injector}.
- * 
+ *
  * @since 3.1
  */
 public abstract class CayenneRuntime {
 
-	/**
-	 * A holder of an Injector bound to the current thread. Used mainly to allow
-	 * serializable contexts to attach to correct Cayenne stack on
-	 * deserialization.
-	 * 
-	 * @since 3.1
-	 */
-	protected static final ThreadLocal<Injector> threadInjector = new ThreadLocal<Injector>();
-
-	/**
-	 * Binds a DI {@link Injector} bound to the current thread. It is primarily
-	 * intended for deserialization of ObjectContexts.
-	 * 
-	 * @since 3.1
-	 */
-	public static void bindThreadInjector(Injector injector) {
-		threadInjector.set(injector);
-	}
-
-	/**
-	 * Returns the {@link Injector} bound to the current thread. Will return
-	 * null if none is bound.
-	 * 
-	 * @since 3.1
-	 */
-	public static Injector getThreadInjector() {
-		return threadInjector.get();
-	}
-
-	protected Injector injector;
-	protected Module module;
-
-	/**
-	 * Creates a CayenneRuntime with configuration based on the supplied array
-	 * of DI modules.
-	 */
-	public CayenneRuntime(Module module) {
-		this.module = module;
-		this.injector = DIBootstrap.createInjector(module);
-	}
-
-	/**
-	 * Returns an array of modules used to initialize this runtime.
-	 * 
-	 * @deprecated since 4.0. We only keep one module now, so use
-	 *             {@link #getModule()}.
-	 */
-	@Deprecated
-	public Module[] getModules() {
-		return new Module[] { module };
-	}
-
-	/**
-	 * 
-	 * Returns the module used to initialize this runtime.
-	 * 
-	 * @since 4.0
-	 */
-	public Module getModule() {
-		return module;
-	}
-
-	/**
-	 * Returns DI injector used by this runtime.
-	 */
-	public Injector getInjector() {
-		return injector;
-	}
-
-	/**
-	 * Shuts down the DI injector of this runtime, giving all services that need
-	 * to release some resources a chance to do that.
-	 */
-	// the following annotation is for environments that manage CayenneRuntimes
-	// within
-	// another DI registry (e.g. unit tests)
-	@BeforeScopeEnd
-	public void shutdown() {
-		injector.shutdown();
-	}
-
-	/**
-	 * Returns the runtime {@link DataChannel}.
-	 */
-	public DataChannel getChannel() {
-		return injector.getInstance(DataChannel.class);
-	}
-
-	/**
-	 * Returns a new ObjectContext instance based on the runtime's main
-	 * DataChannel.
-	 * 
-	 * @since 4.0
-	 */
-	public ObjectContext newContext() {
-		return injector.getInstance(ObjectContextFactory.class).createContext();
-	}
-
-	/**
-	 * Returns a new ObjectContext which is a child of the specified
-	 * DataChannel. This method is used for creation of nested ObjectContexts,
-	 * with parent ObjectContext passed as an argument.
-	 * 
-	 * @since 4.0
-	 */
-	public ObjectContext newContext(DataChannel parentChannel) {
-		return injector.getInstance(ObjectContextFactory.class).createContext(parentChannel);
-	}
-
-	/**
-	 * @deprecated since 3.1 use better named {@link #newContext()} instead.
-	 */
-	@Deprecated
-	public ObjectContext getContext() {
-		return newContext();
-	}
-
-	/**
-	 * @deprecated since 3.1 use better named {@link #newContext(DataChannel)}
-	 *             instead.
-	 */
-	@Deprecated
-	public ObjectContext getContext(DataChannel parentChannel) {
-		return newContext(parentChannel);
-	}
+    /**
+     * A holder of an Injector bound to the current thread. Used mainly to allow
+     * serializable contexts to attach to correct Cayenne stack on
+     * deserialization.
+     *
+     * @since 3.1
+     */
+    protected static final ThreadLocal<Injector> threadInjector = new ThreadLocal<Injector>();
+
+    /**
+     * Binds a DI {@link Injector} bound to the current thread. It is primarily
+     * intended for deserialization of ObjectContexts.
+     *
+     * @since 3.1
+     */
+    public static void bindThreadInjector(Injector injector) {
+        threadInjector.set(injector);
+    }
+
+    /**
+     * Returns the {@link Injector} bound to the current thread. Will return
+     * null if none is bound.
+     *
+     * @since 3.1
+     */
+    public static Injector getThreadInjector() {
+        return threadInjector.get();
+    }
+
+    protected Injector injector;
+    protected Collection<Module> modules;
+
+    /**
+     * Creates a CayenneRuntime with configuration based on the supplied collection of DI modules.
+     */
+    protected CayenneRuntime(Collection<Module> modules) {
+        this.modules = Objects.requireNonNull(modules);
+        this.injector = DIBootstrap.createInjector(modules);
+    }
+
+    /**
+     * Returns the collection of modules used to initialize this runtime.
+     *
+     * @since 4.0
+     */
+    public Collection<Module> getModules() {
+        return modules;
+    }
+
+    /**
+     * Returns DI injector used by this runtime.
+     */
+    public Injector getInjector() {
+        return injector;
+    }
+
+    /**
+     * Shuts down the DI injector of this runtime, giving all services that need
+     * to release some resources a chance to do that.
+     */
+    // the following annotation is for environments that manage CayenneRuntimes
+    // within
+    // another DI registry (e.g. unit tests)
+    @BeforeScopeEnd
+    public void shutdown() {
+        injector.shutdown();
+    }
+
+    /**
+     * Returns the runtime {@link DataChannel}.
+     */
+    public DataChannel getChannel() {
+        return injector.getInstance(DataChannel.class);
+    }
+
+    /**
+     * Returns a new ObjectContext instance based on the runtime's main
+     * DataChannel.
+     *
+     * @since 4.0
+     */
+    public ObjectContext newContext() {
+        return injector.getInstance(ObjectContextFactory.class).createContext();
+    }
+
+    /**
+     * Returns a new ObjectContext which is a child of the specified
+     * DataChannel. This method is used for creation of nested ObjectContexts,
+     * with parent ObjectContext passed as an argument.
+     *
+     * @since 4.0
+     */
+    public ObjectContext newContext(DataChannel parentChannel) {
+        return injector.getInstance(ObjectContextFactory.class).createContext(parentChannel);
+    }
+
+    /**
+     * @deprecated since 3.1 use better named {@link #newContext()} instead.
+     */
+    @Deprecated
+    public ObjectContext getContext() {
+        return newContext();
+    }
+
+    /**
+     * @deprecated since 3.1 use better named {@link #newContext(DataChannel)}
+     * instead.
+     */
+    @Deprecated
+    public ObjectContext getContext(DataChannel parentChannel) {
+        return newContext(parentChannel);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/main/java/org/apache/cayenne/configuration/ModuleCollection.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/ModuleCollection.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/ModuleCollection.java
deleted file mode 100644
index 19c2ed5..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/ModuleCollection.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*****************************************************************
- *   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.configuration;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Module;
-
-/**
- * A module that decorates a collection of other modules. Used as a helper for
- * multi-module runtimes initialization.
- * 
- * @since 4.0
- */
-public class ModuleCollection implements Module {
-
-	private Collection<Module> modules;
-
-	public ModuleCollection(Module... modules) {
-
-		this.modules = new ArrayList<Module>();
-		add(modules);
-	}
-
-	public ModuleCollection add(Module... modules) {
-		if (modules != null) {
-			for (Module m : modules) {
-				addModule(m);
-			}
-		}
-
-		return this;
-	}
-
-	public ModuleCollection add(Collection<Module> modules) {
-		if (modules != null) {
-			for (Module m : modules) {
-				addModule(m);
-			}
-		}
-
-		return this;
-	}
-
-	private void addModule(Module m) {
-		if (m instanceof ModuleCollection) {
-			// flatten
-			add(((ModuleCollection) m).getModules());
-		} else {
-			this.modules.add(m);
-		}
-	}
-
-	public Collection<Module> getModules() {
-		return modules;
-	}
-
-	@Override
-	public void configure(Binder binder) {
-		for (Module m : modules) {
-			m.configure(binder);
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
index 8042949..1e32efc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
@@ -93,6 +93,7 @@ public class ServerModule implements Module {
 
     private static final int DEFAULT_MAX_ID_QUALIFIER_SIZE = 10000;
 
+    @Deprecated
     protected String[] configurationLocations;
 
     /**
@@ -182,16 +183,33 @@ public class ServerModule implements Module {
     }
 
     /**
-     * Creates a ServerModule with at least one configuration location. For
-     * multi-module projects additional locations can be specified as well.
+     * Creates a new {@link ServerModule}.
+     *
+     * @since 4.0
      */
-    public ServerModule(String... configurationLocations) {
+    public ServerModule() {
+        this.configurationLocations = new String[0];
+    }
 
+    /**
+     * Creates a ServerModule with at least one configuration location. For multi-module projects additional locations
+     * can be specified as well.
+     *
+     * @deprecated since 4.0 use {@link ServerRuntimeBuilder#addConfig(String)} and/or
+     * {@link ServerModule#contributeProjectLocations(Binder)} to specify locations.
+     */
+    @Deprecated
+    public ServerModule(String firstConfigLocation, String... configurationLocations) {
         if (configurationLocations == null) {
             configurationLocations = new String[0];
         }
 
-        this.configurationLocations = configurationLocations;
+        this.configurationLocations = new String[configurationLocations.length + 1];
+        this.configurationLocations[0] = firstConfigLocation;
+
+        if(configurationLocations.length > 0) {
+            System.arraycopy(configurationLocations, 0, this.configurationLocations, 1, configurationLocations.length);
+        }
     }
 
     public void configure(Binder binder) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntime.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntime.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntime.java
index 51ec9bf..d6029a2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntime.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntime.java
@@ -21,13 +21,18 @@ package org.apache.cayenne.configuration.server;
 import org.apache.cayenne.access.DataDomain;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.configuration.CayenneRuntime;
-import org.apache.cayenne.configuration.ModuleCollection;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.ListBuilder;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.tx.TransactionListener;
 import org.apache.cayenne.tx.TransactionManager;
 import org.apache.cayenne.tx.TransactionalOperation;
 
 import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static java.util.Arrays.asList;
 
 /**
  * An object representing Cayenne server-stack that connects directly to the
@@ -61,8 +66,28 @@ public class ServerRuntime extends CayenneRuntime {
         return new ServerRuntimeBuilder(name);
     }
 
-    private static ModuleCollection mainModule(String... configurationLocations) {
-        return new ModuleCollection(new ServerModule(configurationLocations));
+    @Deprecated
+    private static Collection<Module> collectModules(final String[] configurationLocations, Module... extraModules) {
+        Collection<Module> modules = new ArrayList<>();
+        modules.add(new ServerModule());
+
+        if(configurationLocations.length > 0) {
+            modules.add(new Module() {
+                @Override
+                public void configure(Binder binder) {
+                    ListBuilder<String> locationsBinder = ServerModule.contributeProjectLocations(binder);
+                    for(String c : configurationLocations) {
+                        locationsBinder.add(c);
+                    }
+                }
+            });
+        }
+
+        if(extraModules != null) {
+            modules.addAll(asList(extraModules));
+        }
+
+        return modules;
     }
 
     /**
@@ -70,9 +95,12 @@ public class ServerRuntime extends CayenneRuntime {
      * contained in {@link ServerModule}. CayenneServerModule is created with
      * provided 'configurationLocation'. An optional array of extra modules may
      * contain service overrides and/or user services.
+     *
+     * @deprecated since 4.0 use {@link ServerRuntime#builder()}.
      */
+    @Deprecated
     public ServerRuntime(String configurationLocation, Module... extraModules) {
-        super(mainModule(configurationLocation).add(extraModules));
+        this(collectModules(new String[]{configurationLocation}, extraModules));
     }
 
     /**
@@ -80,9 +108,24 @@ public class ServerRuntime extends CayenneRuntime {
      * contained in {@link ServerModule}. CayenneServerModule is created with
      * one or more 'configurationLocations'. An optional array of extra modules
      * may contain service overrides and/or user services.
+     *
+     * @deprecated since 4.0 use {@link ServerRuntime#builder()}.
      */
+    @Deprecated
     public ServerRuntime(String[] configurationLocations, Module... extraModules) {
-        super(mainModule(configurationLocations).add(extraModules));
+        this(collectModules(configurationLocations, extraModules));
+    }
+
+    /**
+     * Creates a server runtime configuring it with a standard set of services
+     * contained in {@link ServerModule}. CayenneServerModule is created with
+     * one or more 'configurationLocations'. An optional array of extra modules
+     * may contain service overrides and/or user services.
+     *
+     * @since 4.0
+     */
+    protected ServerRuntime(Collection<Module> modules) {
+        super(modules);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java
index 1c067cd..e5e2ae2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilder.java
@@ -22,6 +22,7 @@ import org.apache.cayenne.access.DataDomain;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.datasource.DataSourceBuilder;
 import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.ListBuilder;
 import org.apache.cayenne.di.MapBuilder;
 import org.apache.cayenne.di.Module;
 
@@ -51,6 +52,7 @@ public class ServerRuntimeBuilder {
     private int jdbcMaxConnections;
     private long maxQueueWaitTime;
     private String validationQuery;
+    private boolean autoLoadModules;
 
     /**
      * @deprecated since 4.0.M5 in favor of {@link ServerRuntime#builder()}
@@ -84,6 +86,19 @@ public class ServerRuntimeBuilder {
         this.configs = new LinkedHashSet<String>();
         this.modules = new ArrayList<Module>();
         this.name = name;
+        this.autoLoadModules = true;
+    }
+
+    /**
+     * Disables DI module auto-loading. By default auto-loading is enabled based on
+     * {@link org.apache.cayenne.di.spi.ModuleLoader} service provider inetrface. If you decide to disable auto-loading,
+     * make sure you provide all the modules that you need.
+     *
+     * @return this builder instance.
+     */
+    public ServerRuntimeBuilder disableModulesAutoLoading() {
+        this.autoLoadModules = false;
+        return this;
     }
 
     /**
@@ -196,20 +211,37 @@ public class ServerRuntimeBuilder {
 
     public ServerRuntime build() {
 
-        buildModules();
+        Collection<Module> configModules = buildConfigModules();
+
+        Collection<Module> allModules = new ArrayList<>();
+        // TODO: make ServerModule auto-loadable?
+        allModules.add(new ServerModule());
+        allModules.addAll(configModules);
+        // custom modules override config modules...
+        allModules.addAll(this.modules);
 
-        String[] configs = this.configs.toArray(new String[this.configs.size()]);
-        Module[] modules = this.modules.toArray(new Module[this.modules.size()]);
-        return new ServerRuntime(configs, modules);
+        return new ServerRuntime(allModules);
     }
 
-    private void buildModules() {
+    private Collection<Module> buildConfigModules() {
 
-        String nameOverride = name;
+        Collection<Module> modules = new ArrayList<>();
 
+        if(!configs.isEmpty()) {
+            modules.add(new Module() {
+                @Override
+                public void configure(Binder binder) {
+                    ListBuilder<String> locationsBinder = ServerModule.contributeProjectLocations(binder);
+                    for(String c : configs) {
+                        locationsBinder.add(c);
+                    }
+                }
+            });
+        }
+
+        String nameOverride = name;
         if (nameOverride == null) {
-            // check if we need to force the default name ... we do when no
-            // configs or multiple configs are supplied.
+            // check if we need to force the default name ... we do when no configs or multiple configs are supplied.
             if (configs.size() != 1) {
                 nameOverride = DEFAULT_NAME;
             }
@@ -218,7 +250,7 @@ public class ServerRuntimeBuilder {
         if (nameOverride != null) {
 
             final String finalNameOverride = nameOverride;
-            prepend(new Module() {
+            modules.add(new Module() {
                 @Override
                 public void configure(Binder binder) {
                     ServerModule.contributeProperties(binder).put(Constants.SERVER_DOMAIN_NAME_PROPERTY, finalNameOverride);
@@ -228,7 +260,7 @@ public class ServerRuntimeBuilder {
 
         if (dataSourceFactory != null) {
 
-            prepend(new Module() {
+            modules.add(new Module() {
                 @Override
                 public void configure(Binder binder) {
                     binder.bind(DataDomain.class).toProvider(SyntheticNodeDataDomainProvider.class);
@@ -237,10 +269,9 @@ public class ServerRuntimeBuilder {
             });
 
         }
-        // URL and driver are the minimal requirement for
-        // DelegatingDataSourceFactory to work
+        // URL and driver are the minimal requirement for DelegatingDataSourceFactory to work
         else if (jdbcUrl != null && jdbcDriver != null) {
-            prepend(new Module() {
+            modules.add(new Module() {
                 @Override
                 public void configure(Binder binder) {
                     binder.bind(DataDomain.class).toProvider(SyntheticNodeDataDomainProvider.class);
@@ -273,11 +304,7 @@ public class ServerRuntimeBuilder {
                 }
             });
         }
-    }
 
-    private void prepend(Module module) {
-        // prepend any special modules BEFORE custom modules, to allow callers
-        // to override our stuff
-        modules.add(0, module);
+        return modules;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
index 8495e99..17c3953 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
@@ -18,18 +18,10 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.rop.server;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
+import com.mockrunner.mock.web.MockServletConfig;
+import com.mockrunner.mock.web.MockServletContext;
 import org.apache.cayenne.configuration.CayenneRuntime;
 import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.configuration.server.ServerModule;
 import org.apache.cayenne.configuration.web.MockModule1;
 import org.apache.cayenne.configuration.web.MockModule2;
@@ -42,8 +34,11 @@ import org.apache.cayenne.rop.ROPServlet;
 import org.junit.After;
 import org.junit.Test;
 
-import com.mockrunner.mock.web.MockServletConfig;
-import com.mockrunner.mock.web.MockServletContext;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.*;
 
 public class ROPServletTest {
 
@@ -122,12 +117,13 @@ public class ROPServletTest {
 
 		assertEquals(Arrays.asList(name + ".xml"), locations);
 		
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(2, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(3, modules.size());
 		Object[] marray = modules.toArray();
 
 		assertTrue(marray[0] instanceof ServerModule);
-		assertTrue(marray[1] instanceof ROPServerModule);
+		// [1] is an inner class
+		assertTrue(marray[2] instanceof ROPServerModule);
 	}
 
 	@Test
@@ -148,15 +144,16 @@ public class ROPServletTest {
 		runtime = WebUtil.getCayenneRuntime(context);
 		assertNotNull(runtime);
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(4, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(5, modules.size());
 
 		Object[] marray = modules.toArray();
 
 		assertTrue(marray[0] instanceof ServerModule);
-		assertTrue(marray[1] instanceof ROPServerModule);
-		assertTrue(marray[2] instanceof MockModule1);
-		assertTrue(marray[3] instanceof MockModule2);
+		// [1] is an inner class
+		assertTrue(marray[2] instanceof ROPServerModule);
+		assertTrue(marray[3] instanceof MockModule1);
+		assertTrue(marray[4] instanceof MockModule2);
 
 		RequestHandler handler = runtime.getInjector().getInstance(RequestHandler.class);
 		assertTrue(handler instanceof MockRequestHandler);
@@ -176,12 +173,12 @@ public class ROPServletTest {
 
 		servlet.init(config);
 		runtime = WebUtil.getCayenneRuntime(context);
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(3, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(4, modules.size());
 
 		Object[] marray = modules.toArray();
 
-		assertTrue(marray[2] instanceof ROPHessianServlet_ConfigModule);
+		assertTrue(marray[3] instanceof ROPHessianServlet_ConfigModule);
 
 		// TODO: mock servlet request to check that the right service instance
 		// is invoked

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java
index 9177fac..2617476 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeBuilderTest.java
@@ -18,24 +18,20 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.server;
 
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.Key;
+import org.apache.cayenne.di.Module;
+import org.junit.After;
+import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ModuleCollection;
-import org.apache.cayenne.di.Key;
-import org.apache.cayenne.di.Module;
-import org.junit.After;
-import org.junit.Test;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
 
 public class ServerRuntimeBuilderTest {
 
@@ -62,9 +58,8 @@ public class ServerRuntimeBuilderTest {
 				Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
 
 		assertEquals(Arrays.asList(), locations);
-		assertTrue(runtime.getModule() instanceof ModuleCollection);
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> modules = runtime.getModules();
 		assertEquals(2, modules.size());
 		assertThat(modules.iterator().next(), instanceOf(ServerModule.class));
 	}
@@ -79,8 +74,8 @@ public class ServerRuntimeBuilderTest {
 
 		assertEquals(Arrays.asList("xxxx"), locations);
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(1, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(2, modules.size());
 		assertThat(modules.iterator().next(), instanceOf(ServerModule.class));
 
 	}
@@ -95,8 +90,8 @@ public class ServerRuntimeBuilderTest {
 
 		assertEquals(Arrays.asList("xxxx", "yyyy"), locations);
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(2, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(3, modules.size());
 		assertThat(modules.iterator().next(), instanceOf(ServerModule.class));
 	}
 
@@ -107,7 +102,7 @@ public class ServerRuntimeBuilderTest {
 
 		runtime = new ServerRuntimeBuilder().addModule(m).build();
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
+		Collection<Module> modules = runtime.getModules();
 		assertEquals(3, modules.size());
 
 		Iterator<Module> it = modules.iterator();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeTest.java
index cee4c67..febbad8 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/ServerRuntimeTest.java
@@ -18,22 +18,11 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.server;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
 import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.QueryResponse;
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.configuration.ObjectContextFactory;
 import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.Key;
@@ -47,159 +36,167 @@ import org.apache.cayenne.tx.TransactionFactory;
 import org.apache.cayenne.tx.TransactionalOperation;
 import org.junit.Test;
 
-public class ServerRuntimeTest {
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 
-	@Test
-	public void testPerformInTransaction() {
+import static java.util.Arrays.asList;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-		final BaseTransaction tx = mock(BaseTransaction.class);
-		final TransactionFactory txFactory = mock(TransactionFactory.class);
-		when(txFactory.createTransaction()).thenReturn(tx);
+public class ServerRuntimeTest {
 
-		Module module = new Module() {
+    @Test
+    public void testPerformInTransaction() {
 
-			public void configure(Binder binder) {
-				binder.bind(TransactionFactory.class).toInstance(txFactory);
-			}
-		};
+        final BaseTransaction tx = mock(BaseTransaction.class);
+        final TransactionFactory txFactory = mock(TransactionFactory.class);
+        when(txFactory.createTransaction()).thenReturn(tx);
 
-		ServerRuntime runtime = new ServerRuntime("xxxx", module);
-		try {
+        Module module = new Module() {
 
-			final Object expectedResult = new Object();
-			Object result = runtime.performInTransaction(new TransactionalOperation<Object>() {
-				public Object perform() {
-					assertSame(tx, BaseTransaction.getThreadTransaction());
-					return expectedResult;
-				}
-			});
+            public void configure(Binder binder) {
+                binder.bind(TransactionFactory.class).toInstance(txFactory);
+            }
+        };
 
-			assertSame(expectedResult, result);
-		} finally {
-			runtime.shutdown();
-		}
+        ServerRuntime runtime = ServerRuntime.builder().addConfig("xxxx").addModule(module).build();
+        try {
 
-	}
+            final Object expectedResult = new Object();
+            Object result = runtime.performInTransaction(new TransactionalOperation<Object>() {
+                public Object perform() {
+                    assertSame(tx, BaseTransaction.getThreadTransaction());
+                    return expectedResult;
+                }
+            });
 
-	@Test
-	public void testDefaultConstructor_SingleLocation() {
-		ServerRuntime runtime = new ServerRuntime("xxxx");
+            assertSame(expectedResult, result);
+        } finally {
+            runtime.shutdown();
+        }
 
-		List<?> locations = runtime.getInjector().getInstance(
-				Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
+    }
 
-		assertEquals(Arrays.asList("xxxx"), locations);
+    @Deprecated
+    @Test
+    public void testDefaultConstructor_SingleLocation() {
+        ServerRuntime runtime = new ServerRuntime("xxxx");
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(1, modules.size());
-		Module m0 = modules.iterator().next();
-		assertTrue(m0 instanceof ServerModule);
-		assertEquals("xxxx", ((ServerModule) m0).configurationLocations[0]);
-	}
+        List<?> locations = runtime.getInjector().getInstance(
+                Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
 
-	@Test
-	public void testDefaultConstructor_MultipleLocations() {
-		ServerRuntime runtime = new ServerRuntime(new String[] { "xxxx", "yyyy" });
+        assertEquals(Arrays.asList("xxxx"), locations);
 
-		List<?> locations = runtime.getInjector().getInstance(
-				Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
+        Collection<Module> modules = runtime.getModules();
+        assertEquals(2, modules.size());
+        Module m0 = modules.iterator().next();
+        assertTrue(m0 instanceof ServerModule);
+    }
 
-		assertEquals(Arrays.asList("xxxx", "yyyy"), locations);
+    @Test
+    @Deprecated
+    public void testDefaultConstructor_MultipleLocations() {
+        ServerRuntime runtime = new ServerRuntime(new String[]{"xxxx", "yyyy"});
 
-		assertTrue(runtime.getModule() instanceof ModuleCollection);
+        List<?> locations = runtime.getInjector().getInstance(
+                Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(1, modules.size());
-		Module m0 = modules.iterator().next();
-		assertTrue(m0 instanceof ServerModule);
+        assertEquals(Arrays.asList("xxxx", "yyyy"), locations);
 
-		assertEquals("xxxx", ((ServerModule) m0).configurationLocations[0]);
-		assertEquals("yyyy", ((ServerModule) m0).configurationLocations[1]);
-	}
+        Collection<Module> modules = runtime.getModules();
+        assertEquals(2, modules.size());
+        Module m0 = modules.iterator().next();
+        assertTrue(m0 instanceof ServerModule);
+    }
 
-	@Test
-	public void testConstructor_Modules() {
+    @Test
+    @Deprecated
+    public void testConstructor_Modules() {
 
-		final boolean[] configured = new boolean[2];
+        final boolean[] configured = new boolean[2];
 
-		Module m1 = new Module() {
+        Module m1 = new Module() {
 
-			public void configure(Binder binder) {
-				configured[0] = true;
-			}
-		};
+            public void configure(Binder binder) {
+                configured[0] = true;
+            }
+        };
 
-		Module m2 = new Module() {
+        Module m2 = new Module() {
 
-			public void configure(Binder binder) {
-				configured[1] = true;
-			}
-		};
+            public void configure(Binder binder) {
+                configured[1] = true;
+            }
+        };
 
-		ServerRuntime runtime = new ServerRuntime("xxxx", m1, m2);
+        ServerRuntime runtime = new ServerRuntime(asList(m1, m2));
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(3, modules.size());
+        Collection<Module> modules = runtime.getModules();
+        assertEquals(2, modules.size());
 
-		assertTrue(configured[0]);
-		assertTrue(configured[1]);
-	}
+        assertTrue(configured[0]);
+        assertTrue(configured[1]);
+    }
 
-	@Test
-	public void testGetDataChannel_CustomModule() {
-		final DataChannel channel = new DataChannel() {
+    @Test
+    @Deprecated
+    public void testGetDataChannel_CustomModule() {
+        final DataChannel channel = new DataChannel() {
 
-			public EntityResolver getEntityResolver() {
-				return null;
-			}
+            public EntityResolver getEntityResolver() {
+                return null;
+            }
 
-			public EventManager getEventManager() {
-				return null;
-			}
+            public EventManager getEventManager() {
+                return null;
+            }
 
-			public QueryResponse onQuery(ObjectContext originatingContext, Query query) {
-				return null;
-			}
+            public QueryResponse onQuery(ObjectContext originatingContext, Query query) {
+                return null;
+            }
 
-			public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, int syncType) {
-				return null;
-			}
-		};
+            public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, int syncType) {
+                return null;
+            }
+        };
 
-		Module module = new Module() {
+        Module module = new Module() {
 
-			public void configure(Binder binder) {
-				binder.bind(DataChannel.class).toInstance(channel);
-			}
-		};
+            public void configure(Binder binder) {
+                binder.bind(DataChannel.class).toInstance(channel);
+            }
+        };
 
-		ServerRuntime runtime = new ServerRuntime("Yuis", module);
-		assertSame(channel, runtime.getChannel());
-	}
+        ServerRuntime runtime = new ServerRuntime("Yuis", module);
+        assertSame(channel, runtime.getChannel());
+    }
 
-	@Test
-	public void testGetObjectContext_CustomModule() {
-		final ObjectContext context = new DataContext();
-		final ObjectContextFactory factory = new ObjectContextFactory() {
+    @Test
+    @Deprecated
+    public void testGetObjectContext_CustomModule() {
+        final ObjectContext context = new DataContext();
+        final ObjectContextFactory factory = new ObjectContextFactory() {
 
-			public ObjectContext createContext(DataChannel parent) {
-				return context;
-			}
+            public ObjectContext createContext(DataChannel parent) {
+                return context;
+            }
 
-			public ObjectContext createContext() {
-				return context;
-			}
-		};
+            public ObjectContext createContext() {
+                return context;
+            }
+        };
 
-		Module module = new Module() {
+        Module module = new Module() {
 
-			public void configure(Binder binder) {
-				binder.bind(ObjectContextFactory.class).toInstance(factory);
-			}
-		};
+            public void configure(Binder binder) {
+                binder.bind(ObjectContextFactory.class).toInstance(factory);
+            }
+        };
 
-		ServerRuntime runtime = new ServerRuntime("mnYw", module);
-		assertSame(context, runtime.newContext());
-		assertSame(context, runtime.newContext());
-	}
+        ServerRuntime runtime = new ServerRuntime("mnYw", module);
+        assertSame(context, runtime.newContext());
+        assertSame(context, runtime.newContext());
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/3dc06bd7/cayenne-server/src/test/java/org/apache/cayenne/configuration/web/CayenneFilterTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/web/CayenneFilterTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/web/CayenneFilterTest.java
index ab8c687..5b73b37 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/web/CayenneFilterTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/web/CayenneFilterTest.java
@@ -25,7 +25,6 @@ import com.mockrunner.mock.web.MockHttpServletResponse;
 import com.mockrunner.mock.web.MockServletContext;
 import org.apache.cayenne.configuration.CayenneRuntime;
 import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ModuleCollection;
 import org.apache.cayenne.configuration.server.ServerModule;
 import org.apache.cayenne.di.Key;
 import org.apache.cayenne.di.Module;
@@ -106,13 +105,14 @@ public class CayenneFilterTest {
 				Key.get(List.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
 
 		assertEquals(Arrays.asList("cayenne-abc.xml"), locations);
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(2, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(3, modules.size());
 
 		Object[] marray = modules.toArray();
 
 		assertTrue(marray[0] instanceof ServerModule);
-		assertTrue(marray[1] instanceof WebModule);
+		// [1] is an inner class
+		assertTrue(marray[2] instanceof WebModule);
 
 		RequestHandler handler = runtime.getInjector().getInstance(RequestHandler.class);
 		assertTrue(handler instanceof SessionContextRequestHandler);
@@ -135,14 +135,15 @@ public class CayenneFilterTest {
 		CayenneRuntime runtime = WebUtil.getCayenneRuntime(context);
 		assertNotNull(runtime);
 
-		Collection<Module> modules = ((ModuleCollection) runtime.getModule()).getModules();
-		assertEquals(4, modules.size());
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(5, modules.size());
 
 		Object[] marray = modules.toArray();
 		assertTrue(marray[0] instanceof ServerModule);
-		assertTrue(marray[1] instanceof WebModule);
-		assertTrue(marray[2] instanceof MockModule1);
-		assertTrue(marray[3] instanceof MockModule2);
+		// [1] is an inner class
+		assertTrue(marray[2] instanceof WebModule);
+		assertTrue(marray[3] instanceof MockModule1);
+		assertTrue(marray[4] instanceof MockModule2);
 
 		RequestHandler handler = runtime.getInjector().getInstance(RequestHandler.class);
 		assertTrue(handler instanceof MockRequestHandler);