You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/12/07 09:00:34 UTC

[19/24] cayenne git commit: CAY-2372. Change package name to previous one.

CAY-2372. Change package name to previous one.


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

Branch: refs/heads/master
Commit: 80416411a7027b1e7b67bd4c42269edc141a40f8
Parents: 37dac62
Author: Arseni Bulatski <an...@gmail.com>
Authored: Wed Nov 8 13:33:12 2017 +0300
Committer: Arseni Bulatski <an...@gmail.com>
Committed: Wed Nov 22 10:26:13 2017 +0300

----------------------------------------------------------------------
 .../osgi/OsgiClassLoaderManager.java            |  75 +++++++
 .../osgi/OsgiDataDomainProvider.java            |  78 +++++++
 .../cayenne/configuration/osgi/OsgiModule.java  |  61 ++++++
 .../configuration/osgi/OsgiModuleBuilder.java   |  86 ++++++++
 .../cayenne/osgi/OsgiClassLoaderManager.java    |  75 -------
 .../cayenne/osgi/OsgiDataDomainProvider.java    |  78 -------
 .../org/apache/cayenne/osgi/OsgiModule.java     |  61 ------
 .../apache/cayenne/osgi/OsgiModuleBuilder.java  |  86 --------
 .../osgi/OsgiClassLoaderManagerTest.java        |  59 +++++
 .../osgi/OsgiClassLoaderManagerTest.java        |  59 -----
 .../rop/server/ROPServerModule.java             |  62 ++++++
 .../java/org/apache/cayenne/rop/ROPServlet.java |   6 +-
 .../cayenne/rop/server/ROPServerModule.java     |  63 ------
 .../rop/server/ROPServerModuleProvider.java     |  29 ---
 .../configuration/rop/server/MockModule1.java   |  30 +++
 .../configuration/rop/server/MockModule2.java   |  32 +++
 .../rop/server/MockRequestHandler.java          |  35 +++
 .../server/ROPHessianServlet_ConfigModule.java  |  38 ++++
 .../rop/server/ROPServletTest.java              | 189 ++++++++++++++++
 .../apache/cayenne/rop/server/MockModule1.java  |  30 ---
 .../apache/cayenne/rop/server/MockModule2.java  |  32 ---
 .../cayenne/rop/server/MockRequestHandler.java  |  35 ---
 .../server/ROPHessianServlet_ConfigModule.java  |  38 ----
 .../cayenne/rop/server/ROPServletTest.java      | 181 ----------------
 .../configuration/web/CayenneFilter.java        | 118 ++++++++++
 .../configuration/web/RequestHandler.java       |  35 +++
 .../web/SessionContextRequestHandler.java       |  88 ++++++++
 .../web/StatelessContextRequestHandler.java     |  72 +++++++
 .../configuration/web/WebConfiguration.java     | 183 ++++++++++++++++
 .../cayenne/configuration/web/WebModule.java    |  35 +++
 .../web/WebServerModuleProvider.java            |  25 +++
 .../cayenne/configuration/web/WebUtil.java      |  54 +++++
 .../org/apache/cayenne/web/CayenneFilter.java   | 124 -----------
 ...iguration.server.CayenneServerModuleProvider |   2 +-
 .../configuration/web/CayenneFilterTest.java    | 179 ++++++++++++++++
 .../web/CayenneFilter_DispatchModule.java       |  29 +++
 .../CayenneFilter_DispatchRequestHandler.java   |  45 ++++
 .../cayenne/configuration/web/MockModule1.java  |  30 +++
 .../cayenne/configuration/web/MockModule2.java  |  31 +++
 .../configuration/web/MockRequestHandler.java   |  33 +++
 .../web/ServletContextHandlerTest.java          | 124 +++++++++++
 .../configuration/web/WebConfigurationTest.java | 214 +++++++++++++++++++
 .../web/WebModuleProviderTest.java              |  30 +++
 .../configuration/web/WebModuleTest.java        |  40 ++++
 .../cayenne/configuration/web/WebUtilTest.java  |  50 +++++
 .../apache/cayenne/web/CayenneFilterTest.java   | 171 ---------------
 .../web/CayenneFilter_DispatchModule.java       |  29 ---
 .../CayenneFilter_DispatchRequestHandler.java   |  45 ----
 .../org/apache/cayenne/web/MockModule1.java     |  30 ---
 .../org/apache/cayenne/web/MockModule2.java     |  31 ---
 .../apache/cayenne/web/MockRequestHandler.java  |  33 ---
 .../cayenne/web/ServletContextHandlerTest.java  | 124 -----------
 .../cayenne/web/WebConfigurationTest.java       | 214 -------------------
 .../cayenne/web/WebModuleProviderTest.java      |  30 ---
 .../org/apache/cayenne/web/WebModuleTest.java   |  40 ----
 .../org/apache/cayenne/web/WebUtilTest.java     |  50 -----
 .../cayenne/tutorial/Http2ROPServlet.java       |   7 +-
 57 files changed, 2168 insertions(+), 1695 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManager.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManager.java b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManager.java
new file mode 100644
index 0000000..2c3e438
--- /dev/null
+++ b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManager.java
@@ -0,0 +1,75 @@
+/*****************************************************************
+ *   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.osgi;
+
+import org.apache.cayenne.di.ClassLoaderManager;
+import org.apache.cayenne.di.Injector;
+
+import java.util.Map;
+
+/**
+ * @since 4.0
+ */
+public class OsgiClassLoaderManager implements ClassLoaderManager {
+
+    private static final String CAYENNE_PACKAGE = "org/apache/cayenne";
+    private static final String CAYENNE_DI_PACKAGE_SUFFIX = "/di";
+
+    private ClassLoader applicationClassLoader;
+    private ClassLoader cayenneServerClassLoader;
+    private ClassLoader cayenneDiClassLoader;
+    private Map<String, ClassLoader> perResourceClassLoaders;
+
+    public OsgiClassLoaderManager(ClassLoader applicationClassLoader, Map<String, ClassLoader> perResourceClassLoaders) {
+        this.applicationClassLoader = applicationClassLoader;
+        this.cayenneDiClassLoader = Injector.class.getClassLoader();
+        this.cayenneServerClassLoader = OsgiClassLoaderManager.class.getClassLoader();
+        this.perResourceClassLoaders = perResourceClassLoaders;
+    }
+
+    @Override
+    public ClassLoader getClassLoader(String resourceName) {
+        if (resourceName == null || resourceName.length() < 2) {
+            return resourceClassLoader(resourceName);
+        }
+
+        String normalizedName = resourceName.charAt(0) == '/' ? resourceName.substring(1) : resourceName;
+        if (normalizedName.startsWith(CAYENNE_PACKAGE)) {
+
+            return (normalizedName.substring(CAYENNE_PACKAGE.length()).startsWith(CAYENNE_DI_PACKAGE_SUFFIX)) ? cayenneDiClassLoader()
+                    : cayenneServerClassLoader();
+        }
+
+        return resourceClassLoader(resourceName);
+    }
+
+    protected ClassLoader resourceClassLoader(String resourceName) {
+        ClassLoader cl = perResourceClassLoaders.get(resourceName);
+        return cl != null ? cl : applicationClassLoader;
+    }
+
+    protected ClassLoader cayenneDiClassLoader() {
+        return cayenneDiClassLoader;
+    }
+
+    protected ClassLoader cayenneServerClassLoader() {
+        return cayenneServerClassLoader;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiDataDomainProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiDataDomainProvider.java b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiDataDomainProvider.java
new file mode 100644
index 0000000..bf1f08e
--- /dev/null
+++ b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiDataDomainProvider.java
@@ -0,0 +1,78 @@
+/*****************************************************************
+ *   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.osgi;
+
+import org.apache.cayenne.ConfigurationException;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.server.DataDomainProvider;
+import org.apache.cayenne.di.ClassLoaderManager;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+
+/**
+ * @since 4.0
+ */
+// TODO: this is really a hack until we can have fully injectable class loading
+// at the EntityResolver level per CAY-1887
+public class OsgiDataDomainProvider extends DataDomainProvider {
+
+    private ClassLoaderManager classLoaderManager;
+
+    public OsgiDataDomainProvider(@Inject ClassLoaderManager classLoaderManager) {
+        this.classLoaderManager = classLoaderManager;
+    }
+
+    @Override
+    public DataDomain get() throws ConfigurationException {
+
+        // here goes the class loading hack, temporarily setting application
+        // bundle ClassLoader to be a thread ClassLoader for runtime to start.
+
+        Thread thread = Thread.currentThread();
+        ClassLoader activeCl = thread.getContextClassLoader();
+        try {
+
+            // using fake package name... as long as it is not
+            // org.apache.cayenne, this do the right trick
+            thread.setContextClassLoader(classLoaderManager.getClassLoader("com/"));
+
+            DataDomain domain = super.get();
+            EntityResolver entityResolver = domain.getEntityResolver();
+            for (ObjEntity e : entityResolver.getObjEntities()) {
+
+                // it is not enough to just call 'getObjectClass()' on
+                // ClassDescriptor - there's an optimization that prevents full
+                // descriptor resolving... so calling some other method...
+                entityResolver.getClassDescriptor(e.getName()).getProperty("__dummy__");
+                entityResolver.getCallbackRegistry();
+            }
+
+            // this triggers callbacks initialization using thread class loader
+            entityResolver.getCallbackRegistry();
+
+            return domain;
+
+        } finally {
+            thread.setContextClassLoader(activeCl);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModule.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModule.java b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModule.java
new file mode 100644
index 0000000..99dbf2f
--- /dev/null
+++ b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModule.java
@@ -0,0 +1,61 @@
+/*****************************************************************
+ *   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.osgi;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.ClassLoaderManager;
+import org.apache.cayenne.di.Module;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A DI module that helps to bootstrap Cayenne in OSGi environment.
+ * 
+ * @since 4.0
+ */
+public class OsgiModule implements Module {
+
+    private Class<?> typeFromProjectBundle;
+    private Map<String, ClassLoader> perTypeClassLoaders;
+
+    OsgiModule() {
+        this.perTypeClassLoaders = new HashMap<>();
+    }
+
+    void setTypeFromProjectBundle(Class<?> typeFromProjectBundle) {
+        this.typeFromProjectBundle = typeFromProjectBundle;
+    }
+
+    void putClassLoader(String type, ClassLoader classLoader) {
+        perTypeClassLoaders.put(type, classLoader);
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        binder.bind(ClassLoaderManager.class).toInstance(createClassLoaderManager());
+        binder.bind(DataDomain.class).toProvider(OsgiDataDomainProvider.class);
+    }
+
+    private ClassLoaderManager createClassLoaderManager() {
+        return new OsgiClassLoaderManager(typeFromProjectBundle.getClassLoader(), perTypeClassLoaders);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModuleBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModuleBuilder.java b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModuleBuilder.java
new file mode 100644
index 0000000..4854461
--- /dev/null
+++ b/cayenne-osgi/src/main/java/org/apache/cayenne/configuration/osgi/OsgiModuleBuilder.java
@@ -0,0 +1,86 @@
+/*****************************************************************
+ *   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.osgi;
+
+import org.apache.cayenne.di.Module;
+
+import java.sql.Driver;
+
+/**
+ * A builder of a DI module that helps to bootstrap Cayenne in OSGi environment.
+ * 
+ * @since 4.0
+ */
+public class OsgiModuleBuilder {
+
+    private OsgiModule module;
+
+    public static OsgiModuleBuilder forProject(Class<?> typeFromProjectBundle) {
+
+        if (typeFromProjectBundle == null) {
+            throw new NullPointerException("Null 'typeFromProjectBundle'");
+        }
+
+        return new OsgiModuleBuilder(typeFromProjectBundle);
+    }
+
+    private OsgiModuleBuilder(Class<?> typeFromProjectBundle) {
+        this.module = new OsgiModule();
+        module.setTypeFromProjectBundle(typeFromProjectBundle);
+    }
+
+    /**
+     * Registers a JDBC driver class used by Cayenne. This is an optional piece
+     * of information used by Cayenne to find JDBC driver in case of Cayenne
+     * managed DataSource. E.g. don't use this when you are using a JNDI
+     * DataSource.
+     */
+    public OsgiModuleBuilder withDriver(Class<? extends Driver> driverType) {
+        return withType(driverType);
+    }
+
+    /**
+     * Registers an arbitrary Java class. If Cayenne tries to load it via
+     * reflection later on, it will be using ClassLoader attached to the class
+     * passed to this method.
+     */
+    public OsgiModuleBuilder withType(Class<?> type) {
+        module.putClassLoader(type.getName(), type.getClassLoader());
+        return this;
+    }
+
+    /**
+     * Registers an arbitrary Java class. If Cayenne tries to load it via
+     * reflection later on, it will be using ClassLoader attached to the class
+     * passed to this method.
+     */
+    public OsgiModuleBuilder withTypes(Class<?>... types) {
+        if (types != null) {
+            for (Class<?> type : types) {
+                module.putClassLoader(type.getName(), type.getClassLoader());
+            }
+        }
+        return this;
+    }
+
+    public Module module() {
+        return module;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiClassLoaderManager.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiClassLoaderManager.java b/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiClassLoaderManager.java
deleted file mode 100644
index eeb7cf9..0000000
--- a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiClassLoaderManager.java
+++ /dev/null
@@ -1,75 +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.osgi;
-
-import java.util.Map;
-
-import org.apache.cayenne.di.ClassLoaderManager;
-import org.apache.cayenne.di.Injector;
-
-/**
- * @since 4.0
- */
-public class OsgiClassLoaderManager implements ClassLoaderManager {
-
-    private static final String CAYENNE_PACKAGE = "org/apache/cayenne";
-    private static final String CAYENNE_DI_PACKAGE_SUFFIX = "/di";
-
-    private ClassLoader applicationClassLoader;
-    private ClassLoader cayenneServerClassLoader;
-    private ClassLoader cayenneDiClassLoader;
-    private Map<String, ClassLoader> perResourceClassLoaders;
-
-    public OsgiClassLoaderManager(ClassLoader applicationClassLoader, Map<String, ClassLoader> perResourceClassLoaders) {
-        this.applicationClassLoader = applicationClassLoader;
-        this.cayenneDiClassLoader = Injector.class.getClassLoader();
-        this.cayenneServerClassLoader = OsgiClassLoaderManager.class.getClassLoader();
-        this.perResourceClassLoaders = perResourceClassLoaders;
-    }
-
-    @Override
-    public ClassLoader getClassLoader(String resourceName) {
-        if (resourceName == null || resourceName.length() < 2) {
-            return resourceClassLoader(resourceName);
-        }
-
-        String normalizedName = resourceName.charAt(0) == '/' ? resourceName.substring(1) : resourceName;
-        if (normalizedName.startsWith(CAYENNE_PACKAGE)) {
-
-            return (normalizedName.substring(CAYENNE_PACKAGE.length()).startsWith(CAYENNE_DI_PACKAGE_SUFFIX)) ? cayenneDiClassLoader()
-                    : cayenneServerClassLoader();
-        }
-
-        return resourceClassLoader(resourceName);
-    }
-
-    protected ClassLoader resourceClassLoader(String resourceName) {
-        ClassLoader cl = perResourceClassLoaders.get(resourceName);
-        return cl != null ? cl : applicationClassLoader;
-    }
-
-    protected ClassLoader cayenneDiClassLoader() {
-        return cayenneDiClassLoader;
-    }
-
-    protected ClassLoader cayenneServerClassLoader() {
-        return cayenneServerClassLoader;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiDataDomainProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiDataDomainProvider.java b/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiDataDomainProvider.java
deleted file mode 100644
index 2981102..0000000
--- a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiDataDomainProvider.java
+++ /dev/null
@@ -1,78 +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.osgi;
-
-import org.apache.cayenne.ConfigurationException;
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.server.DataDomainProvider;
-import org.apache.cayenne.di.ClassLoaderManager;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.ObjEntity;
-
-/**
- * @since 4.0
- */
-// TODO: this is really a hack until we can have fully injectable class loading
-// at the EntityResolver level per CAY-1887
-public class OsgiDataDomainProvider extends DataDomainProvider {
-
-    private ClassLoaderManager classLoaderManager;
-
-    public OsgiDataDomainProvider(@Inject ClassLoaderManager classLoaderManager) {
-        this.classLoaderManager = classLoaderManager;
-    }
-
-    @Override
-    public DataDomain get() throws ConfigurationException {
-
-        // here goes the class loading hack, temporarily setting application
-        // bundle ClassLoader to be a thread ClassLoader for runtime to start.
-
-        Thread thread = Thread.currentThread();
-        ClassLoader activeCl = thread.getContextClassLoader();
-        try {
-
-            // using fake package name... as long as it is not
-            // org.apache.cayenne, this do the right trick
-            thread.setContextClassLoader(classLoaderManager.getClassLoader("com/"));
-
-            DataDomain domain = super.get();
-            EntityResolver entityResolver = domain.getEntityResolver();
-            for (ObjEntity e : entityResolver.getObjEntities()) {
-
-                // it is not enough to just call 'getObjectClass()' on
-                // ClassDescriptor - there's an optimization that prevents full
-                // descriptor resolving... so calling some other method...
-                entityResolver.getClassDescriptor(e.getName()).getProperty("__dummy__");
-                entityResolver.getCallbackRegistry();
-            }
-
-            // this triggers callbacks initialization using thread class loader
-            entityResolver.getCallbackRegistry();
-
-            return domain;
-
-        } finally {
-            thread.setContextClassLoader(activeCl);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModule.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModule.java b/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModule.java
deleted file mode 100644
index 7dd40d3..0000000
--- a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModule.java
+++ /dev/null
@@ -1,61 +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.osgi;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.ClassLoaderManager;
-import org.apache.cayenne.di.Module;
-
-/**
- * A DI module that helps to bootstrap Cayenne in OSGi environment.
- * 
- * @since 4.0
- */
-public class OsgiModule implements Module {
-
-    private Class<?> typeFromProjectBundle;
-    private Map<String, ClassLoader> perTypeClassLoaders;
-
-    OsgiModule() {
-        this.perTypeClassLoaders = new HashMap<>();
-    }
-
-    void setTypeFromProjectBundle(Class<?> typeFromProjectBundle) {
-        this.typeFromProjectBundle = typeFromProjectBundle;
-    }
-
-    void putClassLoader(String type, ClassLoader classLoader) {
-        perTypeClassLoaders.put(type, classLoader);
-    }
-
-    @Override
-    public void configure(Binder binder) {
-        binder.bind(ClassLoaderManager.class).toInstance(createClassLoaderManager());
-        binder.bind(DataDomain.class).toProvider(OsgiDataDomainProvider.class);
-    }
-
-    private ClassLoaderManager createClassLoaderManager() {
-        return new OsgiClassLoaderManager(typeFromProjectBundle.getClassLoader(), perTypeClassLoaders);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModuleBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModuleBuilder.java b/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModuleBuilder.java
deleted file mode 100644
index cb0377b..0000000
--- a/cayenne-osgi/src/main/java/org/apache/cayenne/osgi/OsgiModuleBuilder.java
+++ /dev/null
@@ -1,86 +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.osgi;
-
-import java.sql.Driver;
-
-import org.apache.cayenne.di.Module;
-
-/**
- * A builder of a DI module that helps to bootstrap Cayenne in OSGi environment.
- * 
- * @since 4.0
- */
-public class OsgiModuleBuilder {
-
-    private OsgiModule module;
-
-    public static OsgiModuleBuilder forProject(Class<?> typeFromProjectBundle) {
-
-        if (typeFromProjectBundle == null) {
-            throw new NullPointerException("Null 'typeFromProjectBundle'");
-        }
-
-        return new OsgiModuleBuilder(typeFromProjectBundle);
-    }
-
-    private OsgiModuleBuilder(Class<?> typeFromProjectBundle) {
-        this.module = new OsgiModule();
-        module.setTypeFromProjectBundle(typeFromProjectBundle);
-    }
-
-    /**
-     * Registers a JDBC driver class used by Cayenne. This is an optional piece
-     * of information used by Cayenne to find JDBC driver in case of Cayenne
-     * managed DataSource. E.g. don't use this when you are using a JNDI
-     * DataSource.
-     */
-    public OsgiModuleBuilder withDriver(Class<? extends Driver> driverType) {
-        return withType(driverType);
-    }
-
-    /**
-     * Registers an arbitrary Java class. If Cayenne tries to load it via
-     * reflection later on, it will be using ClassLoader attached to the class
-     * passed to this method.
-     */
-    public OsgiModuleBuilder withType(Class<?> type) {
-        module.putClassLoader(type.getName(), type.getClassLoader());
-        return this;
-    }
-
-    /**
-     * Registers an arbitrary Java class. If Cayenne tries to load it via
-     * reflection later on, it will be using ClassLoader attached to the class
-     * passed to this method.
-     */
-    public OsgiModuleBuilder withTypes(Class<?>... types) {
-        if (types != null) {
-            for (Class<?> type : types) {
-                module.putClassLoader(type.getName(), type.getClassLoader());
-            }
-        }
-        return this;
-    }
-
-    public Module module() {
-        return module;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/test/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManagerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/test/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManagerTest.java b/cayenne-osgi/src/test/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManagerTest.java
new file mode 100644
index 0000000..27ba313
--- /dev/null
+++ b/cayenne-osgi/src/test/java/org/apache/cayenne/configuration/osgi/OsgiClassLoaderManagerTest.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ *   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.osgi;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+
+public class OsgiClassLoaderManagerTest {
+
+    @Test
+    public void testGetClassLoader() {
+
+        final ClassLoader appCl = Mockito.mock(ClassLoader.class);
+        final ClassLoader diCl = Mockito.mock(ClassLoader.class);
+        final ClassLoader serverCl = Mockito.mock(ClassLoader.class);
+
+        OsgiClassLoaderManager manager = new OsgiClassLoaderManager(appCl, Collections.<String, ClassLoader> emptyMap()) {
+            @Override
+            protected ClassLoader cayenneDiClassLoader() {
+                return diCl;
+            }
+
+            @Override
+            protected ClassLoader cayenneServerClassLoader() {
+                return serverCl;
+            }
+        };
+
+        Assert.assertSame(appCl, manager.getClassLoader(null));
+        Assert.assertSame(appCl, manager.getClassLoader(""));
+        Assert.assertSame(appCl, manager.getClassLoader("org/example/test"));
+        Assert.assertSame(appCl, manager.getClassLoader("/org/example/test"));
+        Assert.assertSame(serverCl, manager.getClassLoader("/org/apache/cayenne/access/DataContext.class"));
+        Assert.assertSame(diCl, manager.getClassLoader("/org/apache/cayenne/di/Injector.class"));
+        Assert.assertSame(diCl, manager.getClassLoader("org/apache/cayenne/di/Injector.class"));
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-osgi/src/test/java/org/apache/cayenne/osgi/OsgiClassLoaderManagerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-osgi/src/test/java/org/apache/cayenne/osgi/OsgiClassLoaderManagerTest.java b/cayenne-osgi/src/test/java/org/apache/cayenne/osgi/OsgiClassLoaderManagerTest.java
deleted file mode 100644
index b3d267d..0000000
--- a/cayenne-osgi/src/test/java/org/apache/cayenne/osgi/OsgiClassLoaderManagerTest.java
+++ /dev/null
@@ -1,59 +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.osgi;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import java.util.Collections;
-
-public class OsgiClassLoaderManagerTest {
-
-    @Test
-    public void testGetClassLoader() {
-
-        final ClassLoader appCl = Mockito.mock(ClassLoader.class);
-        final ClassLoader diCl = Mockito.mock(ClassLoader.class);
-        final ClassLoader serverCl = Mockito.mock(ClassLoader.class);
-
-        OsgiClassLoaderManager manager = new OsgiClassLoaderManager(appCl, Collections.<String, ClassLoader> emptyMap()) {
-            @Override
-            protected ClassLoader cayenneDiClassLoader() {
-                return diCl;
-            }
-
-            @Override
-            protected ClassLoader cayenneServerClassLoader() {
-                return serverCl;
-            }
-        };
-
-        Assert.assertSame(appCl, manager.getClassLoader(null));
-        Assert.assertSame(appCl, manager.getClassLoader(""));
-        Assert.assertSame(appCl, manager.getClassLoader("org/example/test"));
-        Assert.assertSame(appCl, manager.getClassLoader("/org/example/test"));
-        Assert.assertSame(serverCl, manager.getClassLoader("/org/apache/cayenne/access/DataContext.class"));
-        Assert.assertSame(diCl, manager.getClassLoader("/org/apache/cayenne/di/Injector.class"));
-        Assert.assertSame(diCl, manager.getClassLoader("org/apache/cayenne/di/Injector.class"));
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
new file mode 100644
index 0000000..ad93183
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ *   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.rop.server;
+
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.MapBuilder;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.remote.RemoteService;
+import org.apache.cayenne.rop.ROPSerializationService;
+import org.apache.cayenne.rop.ServerHessianSerializationServiceProvider;
+import org.apache.cayenne.rop.ServerHttpRemoteService;
+
+import java.util.Map;
+
+/**
+ * A DI module that defines services for the server-side of an ROP application based on
+ * Caucho Hessian.
+ * 
+ * @since 3.1
+ */
+public class ROPServerModule implements Module {
+
+    protected Map<String, String> eventBridgeProperties;
+
+    /**
+     * @since 4.0
+     */
+    public static MapBuilder<String> contributeROPBridgeProperties(Binder binder) {
+        return binder.bindMap(String.class, Constants.SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP);
+    }
+
+    public ROPServerModule(Map<String, String> eventBridgeProperties) {
+        this.eventBridgeProperties = eventBridgeProperties;
+    }
+
+    public void configure(Binder binder) {
+
+        MapBuilder<String> mapBuilder = contributeROPBridgeProperties(binder);
+        mapBuilder.putAll(eventBridgeProperties);
+
+        binder.bind(RemoteService.class).to(ServerHttpRemoteService.class);
+		binder.bind(ROPSerializationService.class).toProvider(ServerHessianSerializationServiceProvider.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
index daa854c..e7a0001 100644
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
@@ -19,10 +19,10 @@
 package org.apache.cayenne.rop;
 
 import org.apache.cayenne.configuration.CayenneRuntime;
-import org.apache.cayenne.rop.server.ROPServerModule;
+import org.apache.cayenne.configuration.rop.server.ROPServerModule;
 import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.apache.cayenne.web.WebConfiguration;
-import org.apache.cayenne.web.WebUtil;
+import org.apache.cayenne.configuration.web.WebConfiguration;
+import org.apache.cayenne.configuration.web.WebUtil;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.remote.ClientMessage;
 import org.apache.cayenne.remote.RemoteService;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModule.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModule.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModule.java
deleted file mode 100644
index fe747b5..0000000
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModule.java
+++ /dev/null
@@ -1,63 +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.rop.server;
-
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.MapBuilder;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.remote.RemoteService;
-import org.apache.cayenne.rop.ROPSerializationService;
-import org.apache.cayenne.rop.ServerHessianSerializationServiceProvider;
-import org.apache.cayenne.rop.ServerHttpRemoteService;
-
-import java.util.Map;
-
-/**
- * A DI module that defines services for the server-side of an ROP application based on
- * Caucho Hessian.
- * 
- * @since 3.1
- */
-public class ROPServerModule implements Module {
-
-    protected Map<String, String> eventBridgeProperties;
-
-    /**
-     * @since 4.0
-     */
-    public static MapBuilder<String> contributeROPBridgeProperties(Binder binder) {
-        return binder.bindMap(String.class, Constants.SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP);
-    }
-
-    public ROPServerModule(Map<String, String> eventBridgeProperties) {
-        this.eventBridgeProperties = eventBridgeProperties;
-    }
-
-    public void configure(Binder binder) {
-
-        MapBuilder<String> mapBuilder = contributeROPBridgeProperties(binder);
-        mapBuilder.putAll(eventBridgeProperties);
-
-        binder.bind(RemoteService.class).to(ServerHttpRemoteService.class);
-		binder.bind(ROPSerializationService.class).toProvider(ServerHessianSerializationServiceProvider.class);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModuleProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModuleProvider.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModuleProvider.java
deleted file mode 100644
index 2aafc9b..0000000
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/rop/server/ROPServerModuleProvider.java
+++ /dev/null
@@ -1,29 +0,0 @@
-//package org.apache.cayenne.rop.server;
-//
-//import org.apache.cayenne.configuration.server.CayenneServerModuleProvider;
-//import org.apache.cayenne.configuration.server.ServerModule;
-//import org.apache.cayenne.di.Module;
-//
-//import java.util.Collection;
-//import java.util.Collections;
-//
-///**
-// * Created by Arseni on 01.11.17.
-// */
-//public class ROPServerModuleProvider implements CayenneServerModuleProvider {
-//    @Override
-//    public Module module() {
-//        return new ROPServerModule();
-//    }
-//
-//    @Override
-//    public Class<? extends Module> moduleType() {
-//        return ROPServerModule.class;
-//    }
-//
-//    @Override
-//    public Collection<Class<? extends Module>> overrides() {
-//        Collection modules = Collections.singletonList(ServerModule.class);
-//        return modules;
-//    }
-//}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule1.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule1.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule1.java
new file mode 100644
index 0000000..f13a585
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule1.java
@@ -0,0 +1,30 @@
+/*****************************************************************
+ *   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.rop.server;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+
+
+public class MockModule1 implements Module {
+
+    public void configure(Binder binder) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule2.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule2.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule2.java
new file mode 100644
index 0000000..7940d6f
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockModule2.java
@@ -0,0 +1,32 @@
+/*****************************************************************
+ *   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.rop.server;
+
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.configuration.web.RequestHandler;
+
+public class MockModule2 implements Module {
+
+    public void configure(Binder binder) {
+        binder.bind(RequestHandler.class).to(MockRequestHandler.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockRequestHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockRequestHandler.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockRequestHandler.java
new file mode 100644
index 0000000..5d401fe
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/MockRequestHandler.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   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.rop.server;
+
+import org.apache.cayenne.configuration.web.RequestHandler;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+public class MockRequestHandler implements RequestHandler {
+
+    public void requestEnd(ServletRequest request, ServletResponse response) {
+    }
+
+    public void requestStart(ServletRequest request, ServletResponse response) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPHessianServlet_ConfigModule.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPHessianServlet_ConfigModule.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPHessianServlet_ConfigModule.java
new file mode 100644
index 0000000..c52d61c
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPHessianServlet_ConfigModule.java
@@ -0,0 +1,38 @@
+/*****************************************************************
+ *   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.rop.server;
+
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.remote.MockRemoteService;
+import org.apache.cayenne.remote.RemoteService;
+
+public class ROPHessianServlet_ConfigModule implements Module {
+
+    public void configure(Binder binder) {
+
+        DataDomain domain = new DataDomain("x");
+        binder.bind(DataChannel.class).toInstance(domain);
+        binder.bind(DataDomain.class).toInstance(domain);
+        binder.bind(RemoteService.class).to(MockRemoteService.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
new file mode 100644
index 0000000..b8e2d32
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/configuration/rop/server/ROPServletTest.java
@@ -0,0 +1,189 @@
+/*****************************************************************
+ *   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.rop.server;
+
+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.server.ServerModule;
+import org.apache.cayenne.configuration.web.*;
+import org.apache.cayenne.di.Key;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.rop.ROPServlet;
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class ROPServletTest {
+
+	private CayenneRuntime runtime;
+
+	@After
+	public void shutdownRuntime() {
+		if(runtime != null) {
+			runtime.shutdown();
+		}
+	}
+
+	@Test
+	public void testInitWithServletName() throws Exception {
+
+		MockServletConfig config = new MockServletConfig();
+		config.setServletName("cayenne-org.apache.cayenne.configuration.rop.server.test-config");
+
+		MockServletContext context = new MockServletContext();
+		config.setServletContext(context);
+
+		ROPServlet servlet = new ROPServlet();
+
+		assertNull(WebUtil.getCayenneRuntime(context));
+		servlet.init(config);
+
+		runtime = WebUtil.getCayenneRuntime(context);
+		assertNotNull(runtime);
+
+		List<String> locations = runtime.getInjector().getInstance(
+				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
+		assertEquals(Arrays.asList("cayenne-org.apache.cayenne.configuration.rop.server.test-config.xml"), locations);
+	}
+
+	@Test
+	public void testInitWithLocation() throws Exception {
+
+		String location = "cayenne-org.apache.cayenne.configuration.rop.server.test-config.xml";
+		MockServletConfig config = new MockServletConfig();
+		config.setServletName("abc");
+		config.setInitParameter("configuration-location", location);
+
+		MockServletContext context = new MockServletContext();
+		config.setServletContext(context);
+
+		ROPServlet servlet = new ROPServlet();
+		servlet.init(config);
+
+		runtime = WebUtil.getCayenneRuntime(context);
+		assertNotNull(runtime);
+		List<String> locations = runtime.getInjector().getInstance(
+				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
+
+		assertEquals(Arrays.asList(location), locations);
+	}
+
+	@Test
+	public void testInitWithStandardModules() throws Exception {
+
+		String name = "cayenne-org.apache.cayenne.configuration.rop.server.test-config";
+
+		MockServletConfig config = new MockServletConfig();
+		config.setServletName(name);
+
+		MockServletContext context = new MockServletContext();
+		config.setServletContext(context);
+
+		ROPServlet servlet = new ROPServlet();
+		servlet.init(config);
+
+		runtime = WebUtil.getCayenneRuntime(context);
+		assertNotNull(runtime);
+
+		List<String> locations = runtime.getInjector().getInstance(
+				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
+
+		assertEquals(Arrays.asList(name + ".xml"), locations);
+
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(4, modules.size());
+		Object[] marray = modules.toArray();
+
+		if(marray[0] instanceof ServerModule){
+			assertTrue(marray[1] instanceof WebModule);
+		}
+		else{
+			assertTrue(marray[0] instanceof WebModule);
+		}
+		assertTrue(marray[2] instanceof ROPServerModule);
+	}
+
+	@Test
+	public void testInitWithExtraModules() throws Exception {
+
+		String name = "cayenne-org.apache.cayenne.configuration.rop.server.test-config";
+
+		MockServletConfig config = new MockServletConfig();
+		config.setServletName(name);
+		config.setInitParameter("extra-modules", MockModule1.class.getName() + "," + MockModule2.class.getName());
+
+		MockServletContext context = new MockServletContext();
+		config.setServletContext(context);
+
+		ROPServlet servlet = new ROPServlet();
+		servlet.init(config);
+
+		runtime = WebUtil.getCayenneRuntime(context);
+		assertNotNull(runtime);
+
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(6, modules.size());
+
+		Object[] marray = modules.toArray();
+		if(marray[0] instanceof ServerModule){
+			assertTrue(marray[1] instanceof WebModule);
+		}
+		else{
+			assertTrue(marray[0] instanceof WebModule);
+		}
+		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);
+	}
+
+	@Test
+	public void testInitHessianService() throws Exception {
+
+		MockServletConfig config = new MockServletConfig();
+		config.setServletName("abc");
+
+		MockServletContext context = new MockServletContext();
+		config.setServletContext(context);
+		config.setInitParameter("extra-modules", ROPHessianServlet_ConfigModule.class.getName());
+
+		ROPServlet servlet = new ROPServlet();
+
+		servlet.init(config);
+		runtime = WebUtil.getCayenneRuntime(context);
+		Collection<Module> modules = runtime.getModules();
+		assertEquals(5, modules.size());
+
+		Object[] marray = modules.toArray();
+
+		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/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule1.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule1.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule1.java
deleted file mode 100644
index f10bec6..0000000
--- a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule1.java
+++ /dev/null
@@ -1,30 +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.rop.server;
-
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Module;
-
-
-public class MockModule1 implements Module {
-
-    public void configure(Binder binder) {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule2.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule2.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule2.java
deleted file mode 100644
index 8dce6a8..0000000
--- a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockModule2.java
+++ /dev/null
@@ -1,32 +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.rop.server;
-
-
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.web.RequestHandler;
-
-public class MockModule2 implements Module {
-
-    public void configure(Binder binder) {
-        binder.bind(RequestHandler.class).to(MockRequestHandler.class);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockRequestHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockRequestHandler.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockRequestHandler.java
deleted file mode 100644
index b57c513..0000000
--- a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/MockRequestHandler.java
+++ /dev/null
@@ -1,35 +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.rop.server;
-
-import org.apache.cayenne.web.RequestHandler;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-
-public class MockRequestHandler implements RequestHandler {
-
-    public void requestEnd(ServletRequest request, ServletResponse response) {
-    }
-
-    public void requestStart(ServletRequest request, ServletResponse response) {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPHessianServlet_ConfigModule.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPHessianServlet_ConfigModule.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPHessianServlet_ConfigModule.java
deleted file mode 100644
index b25da89..0000000
--- a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPHessianServlet_ConfigModule.java
+++ /dev/null
@@ -1,38 +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.rop.server;
-
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.remote.MockRemoteService;
-import org.apache.cayenne.remote.RemoteService;
-
-public class ROPHessianServlet_ConfigModule implements Module {
-
-    public void configure(Binder binder) {
-
-        DataDomain domain = new DataDomain("x");
-        binder.bind(DataChannel.class).toInstance(domain);
-        binder.bind(DataDomain.class).toInstance(domain);
-        binder.bind(RemoteService.class).to(MockRemoteService.class);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPServletTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPServletTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPServletTest.java
deleted file mode 100644
index a68dffd..0000000
--- a/cayenne-rop-server/src/test/java/org/apache/cayenne/rop/server/ROPServletTest.java
+++ /dev/null
@@ -1,181 +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.rop.server;
-
-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.server.ServerModule;
-import org.apache.cayenne.web.*;
-import org.apache.cayenne.di.Key;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.rop.ROPServlet;
-import org.junit.After;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import static org.junit.Assert.*;
-
-public class ROPServletTest {
-
-	private CayenneRuntime runtime;
-
-	@After
-	public void shutdownRuntime() {
-		if(runtime != null) {
-			runtime.shutdown();
-		}
-	}
-
-	@Test
-	public void testInitWithServletName() throws Exception {
-
-		MockServletConfig config = new MockServletConfig();
-		config.setServletName("cayenne-org.apache.cayenne.configuration.rop.server.test-config");
-
-		MockServletContext context = new MockServletContext();
-		config.setServletContext(context);
-
-		ROPServlet servlet = new ROPServlet();
-
-		assertNull(WebUtil.getCayenneRuntime(context));
-		servlet.init(config);
-
-		runtime = WebUtil.getCayenneRuntime(context);
-		assertNotNull(runtime);
-
-		List<String> locations = runtime.getInjector().getInstance(
-				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
-		assertEquals(Arrays.asList("cayenne-org.apache.cayenne.configuration.rop.server.test-config.xml"), locations);
-	}
-
-	@Test
-	public void testInitWithLocation() throws Exception {
-
-		String location = "cayenne-org.apache.cayenne.configuration.rop.server.test-config.xml";
-		MockServletConfig config = new MockServletConfig();
-		config.setServletName("abc");
-		config.setInitParameter("configuration-location", location);
-
-		MockServletContext context = new MockServletContext();
-		config.setServletContext(context);
-
-		ROPServlet servlet = new ROPServlet();
-		servlet.init(config);
-
-		runtime = WebUtil.getCayenneRuntime(context);
-		assertNotNull(runtime);
-		List<String> locations = runtime.getInjector().getInstance(
-				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
-
-		assertEquals(Arrays.asList(location), locations);
-	}
-
-	@Test
-	public void testInitWithStandardModules() throws Exception {
-
-		String name = "cayenne-org.apache.cayenne.configuration.rop.server.test-config";
-
-		MockServletConfig config = new MockServletConfig();
-		config.setServletName(name);
-
-		MockServletContext context = new MockServletContext();
-		config.setServletContext(context);
-
-		ROPServlet servlet = new ROPServlet();
-		servlet.init(config);
-
-		runtime = WebUtil.getCayenneRuntime(context);
-		assertNotNull(runtime);
-
-		List<String> locations = runtime.getInjector().getInstance(
-				Key.getListOf(String.class, Constants.SERVER_PROJECT_LOCATIONS_LIST));
-
-		assertEquals(Arrays.asList(name + ".xml"), locations);
-
-		Collection<Module> modules = runtime.getModules();
-		assertEquals(4, modules.size());
-		Object[] marray = modules.toArray();
-
-//		Now we dont know correct order of modules.
-/*		assertTrue(marray[1] instanceof ServerModule);
-		assertTrue(marray[2] instanceof ROPServerModule);*/
-	}
-
-	@Test
-	public void testInitWithExtraModules() throws Exception {
-
-		String name = "cayenne-org.apache.cayenne.configuration.rop.server.test-config";
-
-		MockServletConfig config = new MockServletConfig();
-		config.setServletName(name);
-		config.setInitParameter("extra-modules", MockModule1.class.getName() + "," + MockModule2.class.getName());
-
-		MockServletContext context = new MockServletContext();
-		config.setServletContext(context);
-
-		ROPServlet servlet = new ROPServlet();
-		servlet.init(config);
-
-		runtime = WebUtil.getCayenneRuntime(context);
-		assertNotNull(runtime);
-
-		Collection<Module> modules = runtime.getModules();
-		assertEquals(6, modules.size());
-
-//		Now we dont know correct order of modules.
-/*		Object[] marray = modules.toArray();
-		assertTrue(marray[1] instanceof ServerModule);
-		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);
-	}
-
-	@Test
-	public void testInitHessianService() throws Exception {
-
-		MockServletConfig config = new MockServletConfig();
-		config.setServletName("abc");
-
-		MockServletContext context = new MockServletContext();
-		config.setServletContext(context);
-		config.setInitParameter("extra-modules", ROPHessianServlet_ConfigModule.class.getName());
-
-		ROPServlet servlet = new ROPServlet();
-
-		servlet.init(config);
-		runtime = WebUtil.getCayenneRuntime(context);
-		Collection<Module> modules = runtime.getModules();
-		assertEquals(5, modules.size());
-
-		Object[] marray = modules.toArray();
-
-		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/80416411/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/CayenneFilter.java
----------------------------------------------------------------------
diff --git a/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/CayenneFilter.java b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/CayenneFilter.java
new file mode 100644
index 0000000..358e640
--- /dev/null
+++ b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/CayenneFilter.java
@@ -0,0 +1,118 @@
+/*****************************************************************
+ *   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.web;
+
+import org.apache.cayenne.configuration.CayenneRuntime;
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Module;
+
+import javax.servlet.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * A filter that creates a Cayenne server runtime, possibly including custom modules. By
+ * default runtime includes {@link ServerModule} and {@link WebModule}. Any custom modules
+ * are loaded after the two standard ones to allow custom service overrides. Filter
+ * initialization parameters:
+ * <ul>
+ * <li>configuration-location - (optional) a name of Cayenne configuration XML file that
+ * will be used to load Cayenne stack. If missing, the filter name will be used to derive
+ * the location. ".xml" extension will be appended to the filter name to get the location,
+ * so a filter named "cayenne-foo" will result in location "cayenne-foo.xml".
+ * <li>extra-modules - (optional) a comma or space-separated list of class names, with
+ * each class implementing {@link Module} interface. These are the custom modules loaded
+ * after the two standard ones that allow users to override any Cayenne runtime aspects,
+ * e.g. {@link RequestHandler}. Each custom module must have a no-arg constructor.
+ * </ul>
+ * <p>
+ * CayenneFilter is a great utility to quickly start a Cayenne application. More advanced
+ * apps most likely will not use it, relying on their own configuration mechanism (such as
+ * Guice, Spring, etc.)
+ * 
+ * @since 3.1
+ */
+public class CayenneFilter implements Filter {
+
+    protected ServletContext servletContext;
+
+    public void init(FilterConfig config) throws ServletException {
+
+        checkAlreadyConfigured(config.getServletContext());
+
+        this.servletContext = config.getServletContext();
+
+        WebConfiguration configAdapter = new WebConfiguration(config);
+
+        String configurationLocation = configAdapter.getConfigurationLocation();
+        Collection<Module> modules = configAdapter.createModules();
+        modules.addAll(getAdditionalModules());
+
+        ServerRuntime runtime = ServerRuntime.builder()
+                .addConfig(configurationLocation)
+                .addModules(modules).build();
+
+        WebUtil.setCayenneRuntime(config.getServletContext(), runtime);
+    }
+
+    /**
+     * Subclasses may override this to specify additional modules that should be included when creating the CayenneRuntime
+     * (in addition to those specified in the web.xml file).
+     *
+     * @since 4.0
+     */
+    protected Collection<Module> getAdditionalModules() {
+		return new ArrayList<>();
+	}
+
+    protected void checkAlreadyConfigured(ServletContext context) throws ServletException {
+        // sanity check
+        if (WebUtil.getCayenneRuntime(context) != null) {
+            throw new ServletException(
+                    "CayenneRuntime is already configured in the servlet environment");
+        }
+    }
+
+    public void destroy() {
+        CayenneRuntime runtime = WebUtil.getCayenneRuntime(servletContext);
+
+        if (runtime != null) {
+            runtime.shutdown();
+        }
+    }
+
+    public void doFilter(
+            ServletRequest request,
+            ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+
+        CayenneRuntime runtime = WebUtil.getCayenneRuntime(servletContext);
+        RequestHandler handler = runtime.getInjector().getInstance(RequestHandler.class);
+
+        handler.requestStart(request, response);
+        try {
+            chain.doFilter(request, response);
+        }
+        finally {
+            handler.requestEnd(request, response);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/RequestHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/RequestHandler.java b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/RequestHandler.java
new file mode 100644
index 0000000..86f41cc
--- /dev/null
+++ b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/RequestHandler.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   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.web;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * A service invoked by {@link CayenneFilter} that provides a callback mechanism to bind
+ * appropriate ObjectContext to the request thread, handle transactions, etc.
+ * 
+ * @since 3.1
+ */
+public interface RequestHandler {
+
+    void requestStart(ServletRequest request, ServletResponse response);
+
+    void requestEnd(ServletRequest request, ServletResponse response);
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/80416411/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/SessionContextRequestHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/SessionContextRequestHandler.java b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/SessionContextRequestHandler.java
new file mode 100644
index 0000000..ea696c6
--- /dev/null
+++ b/cayenne-web/src/main/java/org/apache/cayenne/configuration/web/SessionContextRequestHandler.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ *   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.web;
+
+import org.apache.cayenne.BaseContext;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.CayenneRuntime;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Injector;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * Default implementation of the {@link RequestHandler} that stores per-user
+ * {@link ObjectContext} in a web session and binds it to request thread. Note that using
+ * this handler would force {@link HttpSession} creation, that may not be desirable in
+ * many cases. Also session-bound context may result in a race condition with two user
+ * requests updating the same persistent objects in parallel.
+ * <p>
+ * User applications in most cases should provide a custom RequestHandler that implements
+ * a smarter app-specific strategy for providing ObjectContext.
+ * <p>
+ * For stateless (per request) context creation use {@link StatelessContextRequestHandler}.
+ * 
+ * @since 3.1
+ */
+public class SessionContextRequestHandler implements RequestHandler {
+
+    static final String SESSION_CONTEXT_KEY = SessionContextRequestHandler.class
+            .getName()
+            + ".SESSION_CONTEXT";
+
+    // using injector to lookup services instead of injecting them directly for lazy
+    // startup and "late binding"
+    @Inject
+    private Injector injector;
+
+    public void requestStart(ServletRequest request, ServletResponse response) {
+
+        CayenneRuntime.bindThreadInjector(injector);
+
+        if (request instanceof HttpServletRequest) {
+
+            // this forces session creation if it does not exist yet
+            HttpSession session = ((HttpServletRequest) request).getSession();
+
+            ObjectContext context;
+            synchronized (session) {
+                context = (ObjectContext) session.getAttribute(SESSION_CONTEXT_KEY);
+
+                if (context == null) {
+                    context = injector
+                            .getInstance(ObjectContextFactory.class)
+                            .createContext();
+                    session.setAttribute(SESSION_CONTEXT_KEY, context);
+                }
+            }
+
+            BaseContext.bindThreadObjectContext(context);
+        }
+    }
+
+    public void requestEnd(ServletRequest request, ServletResponse response) {
+        CayenneRuntime.bindThreadInjector(null);
+        BaseContext.bindThreadObjectContext(null);
+    }
+
+}