Provide ability to add extra modules in declarative way
diff --git a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenCaseModule.java b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenCaseModule.java
index a0cd04e..7ac4dc2 100644
--- a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenCaseModule.java
+++ b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenCaseModule.java
@@ -24,6 +24,7 @@
 import org.apache.cayenne.di.spi.DefaultScope;
 import org.apache.cayenne.gen.mock.CustomPropertyDescriptor;
 import org.apache.cayenne.unit.di.UnitTestLifecycleManager;
+import org.apache.cayenne.unit.di.server.ServerCaseExtraModulesProperties;
 import org.apache.cayenne.unit.di.server.ServerCaseLifecycleManager;
 import org.apache.cayenne.unit.di.server.ServerCaseProperties;
 
@@ -41,6 +42,7 @@
     public void configure(Binder binder) {
         binder.bind(UnitTestLifecycleManager.class).toInstance(new ServerCaseLifecycleManager(testScope));
         binder.bind(ServerCaseProperties.class).to(ServerCaseProperties.class).in(testScope);
+        binder.bind(ServerCaseExtraModulesProperties.class).to(ServerCaseExtraModulesProperties.class).in(testScope);
 
         CgenModule.contributeUserProperties(binder)
                 .add(CustomPropertyDescriptor.class);
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/unit/DbSyncServerRuntimeProvider.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/unit/DbSyncServerRuntimeProvider.java
index eb67d0e..9b37e38 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/unit/DbSyncServerRuntimeProvider.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/unit/DbSyncServerRuntimeProvider.java
@@ -28,6 +28,7 @@
 import org.apache.cayenne.di.Provider;
 import org.apache.cayenne.unit.UnitDbAdapter;
 import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
+import org.apache.cayenne.unit.di.server.ServerCaseExtraModulesProperties;
 import org.apache.cayenne.unit.di.server.ServerCaseProperties;
 import org.apache.cayenne.unit.di.server.ServerRuntimeProvider;
 
@@ -35,9 +36,10 @@
 
     public DbSyncServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
                                        @Inject ServerCaseProperties properties,
+                                       @Inject ServerCaseExtraModulesProperties extraModulesProperties,
                                        @Inject Provider<DbAdapter> dbAdapterProvider,
                                        @Inject UnitDbAdapter unitDbAdapter) {
-        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+        super(dataSourceFactory, properties, extraModulesProperties, dbAdapterProvider, unitDbAdapter);
     }
 
     @Override
diff --git a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
index 5b4fd6e..99af30e 100644
--- a/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
+++ b/cayenne-jcache/src/test/java/org/apache/cayenne/jcache/unit/CacheServerRuntimeProvider.java
@@ -27,6 +27,7 @@
 import org.apache.cayenne.jcache.JCacheModule;
 import org.apache.cayenne.unit.UnitDbAdapter;
 import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
+import org.apache.cayenne.unit.di.server.ServerCaseExtraModulesProperties;
 import org.apache.cayenne.unit.di.server.ServerCaseProperties;
 import org.apache.cayenne.unit.di.server.ServerRuntimeProvider;
 
@@ -38,9 +39,10 @@
 
     public CacheServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
                                       @Inject ServerCaseProperties properties,
+                                      @Inject ServerCaseExtraModulesProperties extraModulesProperties,
                                       @Inject Provider<DbAdapter> dbAdapterProvider,
                                       @Inject UnitDbAdapter unitDbAdapter) {
-        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+        super(dataSourceFactory, properties, extraModulesProperties, dbAdapterProvider, unitDbAdapter);
     }
 
     @Override
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/CDOReflexiveRelICustomSorterIT.java b/cayenne-server/src/test/java/org/apache/cayenne/CDOReflexiveRelICustomSorterIT.java
index 11b9c3d..b3a28e2 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/CDOReflexiveRelICustomSorterIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/CDOReflexiveRelICustomSorterIT.java
@@ -19,7 +19,12 @@
 
 package org.apache.cayenne;
 
+import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
+import org.apache.cayenne.access.flush.operation.GraphBasedDbRowOpSorter;
+import org.apache.cayenne.di.Binder;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.unit.di.server.InjectExtraModules;
+import org.apache.cayenne.di.Module;
 import org.apache.cayenne.query.ObjectSelect;
 import org.apache.cayenne.testdo.testmap.ArtGroup;
 import org.apache.cayenne.testdo.testmap.Artist;
@@ -27,13 +32,14 @@
 import org.apache.cayenne.testdo.testmap.Gallery;
 import org.apache.cayenne.testdo.testmap.Painting;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
-import org.apache.cayenne.unit.di.server.ServerCaseCustomSorter;
+import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.junit.Ignore;
 import org.junit.Test;
 
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
-public class CDOReflexiveRelICustomSorterIT extends ServerCaseCustomSorter {
+@InjectExtraModules(extraModules = {CDOReflexiveRelICustomSorterIT.CustomServerCase.class})
+public class CDOReflexiveRelICustomSorterIT extends ServerCase {
     @Inject
     private ObjectContext context;
 
@@ -187,4 +193,13 @@
         context.deleteObject(artist2);
         context.commitChanges();
     }
+
+    protected static class CustomServerCase implements Module {
+        public CustomServerCase() {}
+
+        @Override
+        public void configure(Binder binder) {
+            binder.bind(DbRowOpSorter.class).to(GraphBasedDbRowOpSorter.class);
+        }
+    }
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/InjectExtraModules.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/InjectExtraModules.java
new file mode 100644
index 0000000..94153ba
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/InjectExtraModules.java
@@ -0,0 +1,40 @@
+/*****************************************************************
+  *   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
+  *
+  *    https://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.unit.di.server;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+@Target(TYPE)
+@Documented
+@Inherited
+/**
+ * Annotation provides the ability to add additional modules in declarative way
+ *
+ * @since 4.3
+ */
+public @interface InjectExtraModules {
+    Class[] extraModules() default {};
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseExtraModulesProperties.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseExtraModulesProperties.java
new file mode 100644
index 0000000..75a6b58
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseExtraModulesProperties.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ *   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
+ *
+ *    https://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.unit.di.server;
+
+import org.apache.cayenne.di.Module;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @since 4.3
+ */
+public class ServerCaseExtraModulesProperties {
+    protected Class[] extraModules;
+
+    public Collection<? extends Module> getExtraModules() {
+        Collection<Module> result = new ArrayList<>();
+        for (Class extraModule : extraModules) {
+            try {
+                result.add((Module) extraModule.getConstructor().newInstance());
+            } catch (InstantiationException | IllegalAccessException |
+                     InvocationTargetException | NoSuchMethodException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return result;
+    }
+
+    public void setExtraModules(Class[] extraModules) {
+        this.extraModules = extraModules;
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseLifecycleManager.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseLifecycleManager.java
index a24b4b1..0911721 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseLifecycleManager.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseLifecycleManager.java
@@ -28,6 +28,9 @@
     @Inject
     protected Provider<ServerCaseProperties> propertiesProvider;
 
+    @Inject
+    protected Provider<ServerCaseExtraModulesProperties> extraModulesPropertiesProvider;
+
     public ServerCaseLifecycleManager(DefaultScope scope) {
         super(scope);
     }
@@ -39,9 +42,15 @@
         UseServerRuntime runtimeName = testCase.getClass().getAnnotation(
                 UseServerRuntime.class);
 
+        InjectExtraModules injectExtraModules = testCase.getClass().getAnnotation(
+                InjectExtraModules.class);
+
         String location = runtimeName != null ? runtimeName.value() : null;
         propertiesProvider.get().setConfigurationLocation(location);
 
+        Class[] modules = injectExtraModules != null ? injectExtraModules.extraModules() : new Class[]{};
+        extraModulesPropertiesProvider.get().setExtraModules(modules);
+
         super.setUp(testCase);
     }
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
index 417e378..a5dff12 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
@@ -262,6 +262,7 @@
         binder.bind(EntityResolver.class).toProvider(ServerCaseEntityResolverProvider.class).in(testScope);
         binder.bind(DataNode.class).toProvider(ServerCaseDataNodeProvider.class).in(testScope);
         binder.bind(ServerCaseProperties.class).to(ServerCaseProperties.class).in(testScope);
+        binder.bind(ServerCaseExtraModulesProperties.class).to(ServerCaseExtraModulesProperties.class).in(testScope);
         binder.bind(ServerRuntime.class).toProvider(ServerRuntimeProvider.class).in(testScope);
         binder.bind(ObjectContext.class).toProvider(ServerCaseObjectContextProvider.class).withoutScope();
         binder.bind(DataContext.class).toProvider(ServerCaseDataContextProvider.class).withoutScope();
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProvider.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProvider.java
index c14202f..d4219ff 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProvider.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProvider.java
@@ -38,6 +38,7 @@
 public class ServerRuntimeProvider implements Provider<ServerRuntime> {
 
     private ServerCaseProperties properties;
+    private ServerCaseExtraModulesProperties extraModulesProperties;
     private ServerCaseDataSourceFactory dataSourceFactory;
     private UnitDbAdapter unitDbAdapter;
 
@@ -45,11 +46,13 @@
 
     public ServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
             @Inject ServerCaseProperties properties,
+            @Inject ServerCaseExtraModulesProperties extraModulesProperties,
             @Inject Provider<DbAdapter> dbAdapterProvider,
             @Inject UnitDbAdapter unitDbAdapter) {
 
         this.dataSourceFactory = dataSourceFactory;
         this.properties = properties;
+        this.extraModulesProperties = extraModulesProperties;
         this.dbAdapterProvider = dbAdapterProvider;
         this.unitDbAdapter = unitDbAdapter;
     }
@@ -65,6 +68,8 @@
 
         Collection<Module> modules = new ArrayList<>(getExtraModules());
 
+        modules.addAll(extraModulesProperties.getExtraModules());
+
         return ServerRuntime.builder()
                         .addConfig(configurationLocation)
                         .addModules(modules)
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProviderContextsSync.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProviderContextsSync.java
index fc68d22..7b15452 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProviderContextsSync.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerRuntimeProviderContextsSync.java
@@ -35,9 +35,10 @@
 
     public ServerRuntimeProviderContextsSync(@Inject ServerCaseDataSourceFactory dataSourceFactory,
                                              @Inject ServerCaseProperties properties,
+                                             @Inject ServerCaseExtraModulesProperties extraModulesProperties,
                                              @Inject Provider<DbAdapter> dbAdapterProvider,
                                              @Inject UnitDbAdapter unitDbAdapter) {
-        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+        super(dataSourceFactory, properties, extraModulesProperties, dbAdapterProvider, unitDbAdapter);
     }
 
     @Override
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/WeakReferenceStrategyServerRuntimeProvider.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/WeakReferenceStrategyServerRuntimeProvider.java
index f00a5a6..c07cf9d 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/WeakReferenceStrategyServerRuntimeProvider.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/WeakReferenceStrategyServerRuntimeProvider.java
@@ -33,9 +33,10 @@
 
     public WeakReferenceStrategyServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
                                                       @Inject ServerCaseProperties properties,
+                                                      @Inject ServerCaseExtraModulesProperties extraModulesProperties,
                                                       @Inject Provider<DbAdapter> dbAdapterProvider,
                                                       @Inject UnitDbAdapter unitDbAdapter) {
-        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+        super(dataSourceFactory, properties, extraModulesProperties, dbAdapterProvider, unitDbAdapter);
     }
 
     @Override
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/util/CustomSorterServerRuntimeProvider.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/util/CustomSorterServerRuntimeProvider.java
index 3191ec3..f288728 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/util/CustomSorterServerRuntimeProvider.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/util/CustomSorterServerRuntimeProvider.java
@@ -27,6 +27,7 @@
 import org.apache.cayenne.di.Provider;
 import org.apache.cayenne.unit.UnitDbAdapter;
 import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
+import org.apache.cayenne.unit.di.server.ServerCaseExtraModulesProperties;
 import org.apache.cayenne.unit.di.server.ServerCaseProperties;
 import org.apache.cayenne.unit.di.server.ServerRuntimeProvider;
 
@@ -36,9 +37,10 @@
 public class CustomSorterServerRuntimeProvider extends ServerRuntimeProvider {
     public CustomSorterServerRuntimeProvider(@Inject ServerCaseDataSourceFactory dataSourceFactory,
                                              @Inject ServerCaseProperties properties,
+                                             @Inject ServerCaseExtraModulesProperties extraModulesProperties,
                                              @Inject Provider<DbAdapter> dbAdapterProvider,
                                              @Inject UnitDbAdapter unitDbAdapter) {
-        super(dataSourceFactory, properties, dbAdapterProvider, unitDbAdapter);
+        super(dataSourceFactory, properties, extraModulesProperties, dbAdapterProvider, unitDbAdapter);
     }
 
     @Override