You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by dk...@apache.org on 2016/03/01 15:50:40 UTC

[2/9] cayenne git commit: Initial implementation of ROP refactoring decoupling Hessian from connectivity layer and introducing pluggable serialization and connectivity layers.

Initial implementation of ROP refactoring decoupling Hessian from connectivity layer and introducing pluggable serialization and connectivity layers.


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

Branch: refs/heads/master
Commit: 83185f2558f764cf60b137a2e2eeda47d366a17f
Parents: f72db8b
Author: Dzmitry Kazimirchyk <dk...@gmail.com>
Authored: Tue Jan 19 01:38:28 2016 +0300
Committer: Dzmitry Kazimirchyk <dk...@gmail.com>
Committed: Tue Mar 1 12:49:03 2016 +0300

----------------------------------------------------------------------
 .../configuration/rop/client/ClientModule.java  |   9 +-
 .../rop/client/HessianConnectionProvider.java   |  63 -----
 .../apache/cayenne/remote/BaseConnection.java   |  21 --
 .../remote/hessian/ClientSerializerFactory.java |   2 +-
 .../remote/hessian/HessianConnection.java       |   2 +-
 .../cayenne/rop/DefaultClientConnection.java    | 122 ++++++++++
 .../rop/DefaultClientConnectionProvider.java    |  44 ++++
 .../apache/cayenne/rop/ProxyRemoteService.java  |  59 +++++
 .../org/apache/cayenne/rop/ROPConnector.java    |  32 +++
 ...ientHessianSerializationServiceProvider.java |  39 ++++
 .../cayenne/rop/http/HttpROPConnector.java      | 233 +++++++++++++++++++
 .../rop/http/HttpROPConnectorProvider.java      |  58 +++++
 .../rop/client/ClientModuleTest.java            |   4 +-
 .../rop/server/ROPServerModule.java             |   7 +-
 .../service/ServerSerializerFactory.java        |   2 +-
 .../rop/HessianROPSerializationService.java     |  69 ++++++
 .../org/apache/cayenne/rop/ROPConstants.java    |  28 +++
 .../cayenne/rop/ROPSerializationService.java    |  34 +++
 .../java/org/apache/cayenne/rop/ROPServlet.java | 122 ++++++++++
 ...rverHessianSerializationServiceProvider.java |  37 +++
 .../cayenne/rop/ServerHttpRemoteService.java    |  48 ++++
 21 files changed, 943 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientModule.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientModule.java b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientModule.java
index de64f08..041408a 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientModule.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/ClientModule.java
@@ -32,6 +32,10 @@ import org.apache.cayenne.di.Module;
 import org.apache.cayenne.event.DefaultEventManager;
 import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.remote.ClientConnection;
+import org.apache.cayenne.remote.RemoteService;
+import org.apache.cayenne.rop.*;
+import org.apache.cayenne.rop.http.ClientHessianSerializationServiceProvider;
+import org.apache.cayenne.rop.http.HttpROPConnectorProvider;
 
 /**
  * A DI module containing all Cayenne ROP client runtime configurations.
@@ -56,7 +60,10 @@ public class ClientModule implements Module {
         binder.<String> bindMap(Constants.PROPERTIES_MAP).putAll(properties);
 
         binder.bind(ObjectContextFactory.class).to(CayenneContextFactory.class);
-        binder.bind(ClientConnection.class).toProvider(HessianConnectionProvider.class);
+        binder.bind(ROPSerializationService.class).toProvider(ClientHessianSerializationServiceProvider.class);
+        binder.bind(ROPConnector.class).toProvider(HttpROPConnectorProvider.class);
+        binder.bind(RemoteService.class).to(ProxyRemoteService.class);
+        binder.bind(ClientConnection.class).toProvider(DefaultClientConnectionProvider.class);
         binder.bind(EventManager.class).to(DefaultEventManager.class);
         binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
         binder.bind(DataChannel.class).toProvider(ClientChannelProvider.class);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/HessianConnectionProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/HessianConnectionProvider.java b/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/HessianConnectionProvider.java
deleted file mode 100644
index 21fda18..0000000
--- a/cayenne-client/src/main/java/org/apache/cayenne/configuration/rop/client/HessianConnectionProvider.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.configuration.rop.client;
-
-import org.apache.cayenne.ConfigurationException;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Provider;
-import org.apache.cayenne.remote.ClientConnection;
-import org.apache.cayenne.remote.hessian.HessianConnection;
-
-public class HessianConnectionProvider implements Provider<ClientConnection> {
-
-    @Inject
-    protected RuntimeProperties runtimeProperties;
-
-    public ClientConnection get() throws ConfigurationException {
-
-        String url = runtimeProperties.get(Constants.ROP_SERVICE_URL_PROPERTY);
-        if (url == null) {
-            throw new ConfigurationException(
-                    "No property defined for '%s', can't initialize HessianConnection",
-                    Constants.ROP_SERVICE_URL_PROPERTY);
-        }
-
-        String userName = runtimeProperties.get(Constants.ROP_SERVICE_USERNAME_PROPERTY);
-        String password = runtimeProperties.get(Constants.ROP_SERVICE_PASSWORD_PROPERTY);
-        String sharedSession = runtimeProperties
-                .get(Constants.ROP_SERVICE_SHARED_SESSION_PROPERTY);
-        long readTimeout = runtimeProperties.getLong(
-                Constants.ROP_SERVICE_TIMEOUT_PROPERTY,
-                -1l);
-
-        HessianConnection result = new HessianConnection(
-                url,
-                userName,
-                password,
-                sharedSession);
-
-        if (readTimeout > 0) {
-            result.setReadTimeout(readTimeout);
-        }
-
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/remote/BaseConnection.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/remote/BaseConnection.java b/cayenne-client/src/main/java/org/apache/cayenne/remote/BaseConnection.java
index 74dd3f4..d02449e 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/remote/BaseConnection.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/remote/BaseConnection.java
@@ -33,7 +33,6 @@ public abstract class BaseConnection implements ClientConnection {
 
     protected Log logger;
     protected long messageId;
-    protected long readTimeout = 0L;
     
     /**
      * Default constructor that initializes logging and a single threaded EventManager.
@@ -111,26 +110,6 @@ public abstract class BaseConnection implements ClientConnection {
     public long getProcessedMessagesCount() {
         return messageId + 1;
     }
-
-    /**
-     * The socket timeout on requests in milliseconds. Defaults to infinity.
-     * 
-     * @since 3.1
-     */
-    public long getReadTimeout() {
-        return readTimeout;
-    }
-    
-    /**
-     * Sets the socket timeout.
-     * 
-     * @param readTimeout The socket timeout on requests in milliseconds.
-     * 
-     * @since 3.1
-     */
-    public void setReadTimeout(long readTimeout) {
-        this.readTimeout = readTimeout;
-    }
     
     /**
      * Called before logging the beginning of message processing.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/ClientSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/ClientSerializerFactory.java b/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/ClientSerializerFactory.java
index f39d642..ff7aeb3 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/ClientSerializerFactory.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/ClientSerializerFactory.java
@@ -34,7 +34,7 @@ import com.caucho.hessian.io.Serializer;
  * 
  * @since 1.2
  */
-class ClientSerializerFactory extends AbstractSerializerFactory {
+public class ClientSerializerFactory extends AbstractSerializerFactory {
 
     private Deserializer dataRowDeserializer;
     private Deserializer listDeserializer;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/HessianConnection.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/HessianConnection.java b/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/HessianConnection.java
index d8f6ee2..3bea90d 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/HessianConnection.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/remote/hessian/HessianConnection.java
@@ -226,7 +226,7 @@ public class HessianConnection extends BaseConnection {
         factory.setConnectionFactory(new HessianURLConnectionFactory(this));
         factory.setUser(userName);
         factory.setPassword(password);
-        factory.setReadTimeout(getReadTimeout());
+//        factory.setReadTimeout(getReadTimeout());
 
         this.serializerFactory = factory.getSerializerFactory();
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnection.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnection.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnection.java
new file mode 100644
index 0000000..2004c6e
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnection.java
@@ -0,0 +1,122 @@
+/*****************************************************************
+ *   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;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.event.EventBridge;
+import org.apache.cayenne.event.EventBridgeFactory;
+import org.apache.cayenne.remote.BaseConnection;
+import org.apache.cayenne.remote.ClientMessage;
+import org.apache.cayenne.remote.RemoteService;
+import org.apache.cayenne.remote.RemoteSession;
+
+public class DefaultClientConnection extends BaseConnection {
+
+	private RemoteService remoteService;
+	private RemoteSession session;
+
+	private String sharedSessionName;
+    
+    public DefaultClientConnection(RemoteService remoteService, String sharedSession) {
+        this.remoteService = remoteService;
+        this.sharedSessionName = sharedSession;
+    }
+
+	@Override
+	protected void beforeSendMessage(ClientMessage message) throws CayenneRuntimeException {
+		if (session == null) {
+			connect();
+		}
+	}
+
+	@Override
+	protected Object doSendMessage(ClientMessage message) throws CayenneRuntimeException {
+        try {
+            return remoteService.processMessage(message);
+        }
+        catch (CayenneRuntimeException e) {
+            throw e;
+        }
+        catch (Throwable th) {
+            throw new CayenneRuntimeException(th.getMessage(), th);
+        }
+	}
+
+	@Override
+	public EventBridge getServerEventBridge() throws CayenneRuntimeException {
+        if (session == null) {
+            connect();
+        }
+
+        return createServerEventBridge(session);
+	}
+
+	protected synchronized void connect() {
+		if (session != null) {
+			return;
+		}
+        
+        long t0 = System.currentTimeMillis();
+
+		// create server session...
+		try {
+			this.session = (sharedSessionName != null) ? remoteService
+					.establishSharedSession(sharedSessionName) : remoteService
+					.establishSession();
+		}
+		catch (Throwable th) {
+			logger.info(th.getMessage(), th);
+			throw new CayenneRuntimeException(th.getMessage(), th);
+		}
+
+        if (logger.isInfoEnabled()) {
+            long time = System.currentTimeMillis() - t0;
+            logger.info("=== Connected, session: "
+                    + session
+                    + " - took "
+                    + time
+                    + " ms.");
+        }
+	}
+
+    /**
+     * Creates an EventBridge that will listen for server events. Returns null if server
+     * events support is not configured in the descriptor.
+     *
+     * @throws CayenneRuntimeException if EventBridge startup fails for any reason.
+     */
+    protected EventBridge createServerEventBridge(RemoteSession session) throws CayenneRuntimeException {
+
+        if (!session.isServerEventsEnabled()) {
+            return null;
+        }
+
+        try {
+            EventBridgeFactory factory = (EventBridgeFactory) Class.forName(session.getEventBridgeFactory())
+                    .newInstance();
+
+            // must use "name", not the sessionId as an external subject for the
+            // event bridge
+            return factory.createEventBridge(RemoteSession.getSubjects(), session.getName(),
+                    session.getEventBridgeParameters());
+        } catch (Exception ex) {
+            throw new CayenneRuntimeException("Error creating EventBridge.", ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnectionProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnectionProvider.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnectionProvider.java
new file mode 100644
index 0000000..a6dc9e5
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/DefaultClientConnectionProvider.java
@@ -0,0 +1,44 @@
+/*****************************************************************
+ *   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;
+
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.remote.ClientConnection;
+import org.apache.cayenne.remote.RemoteService;
+
+public class DefaultClientConnectionProvider implements Provider<ClientConnection> {
+
+    @Inject
+    protected RuntimeProperties runtimeProperties;
+    
+    @Inject
+    protected RemoteService remoteService;
+
+    @Override
+    public ClientConnection get() throws DIRuntimeException {
+        String sharedSession = runtimeProperties
+                .get(Constants.ROP_SERVICE_SHARED_SESSION_PROPERTY);
+
+        return new DefaultClientConnection(remoteService, sharedSession);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java
new file mode 100644
index 0000000..ecfb441
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.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.rop;
+
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.remote.ClientMessage;
+import org.apache.cayenne.remote.RemoteService;
+import org.apache.cayenne.remote.RemoteSession;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+
+public class ProxyRemoteService implements RemoteService {
+
+	@Inject
+	protected ROPSerializationService serializationService;
+	
+	@Inject
+	protected ROPConnector ropConnector;
+	
+	@Override
+	public RemoteSession establishSession() throws RemoteException {
+        try {
+            return serializationService.deserialize(ropConnector.establishSession(), RemoteSession.class);
+        } catch (IOException e) {
+            throw new RemoteException(e.getMessage());
+        }
+	}
+
+	@Override
+	public RemoteSession establishSharedSession(String name) throws RemoteException {
+        try {
+            return serializationService.deserialize(ropConnector.establishSharedSession(name), RemoteSession.class);
+        } catch (IOException e) {
+            throw new RemoteException(e.getMessage());
+        }
+	}
+
+	@Override
+	public Object processMessage(ClientMessage message) throws RemoteException, Throwable {
+		return serializationService.deserialize(ropConnector.sendMessage(serializationService.serialize(message)), Object.class);
+	}
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java
new file mode 100644
index 0000000..4976d80
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.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.rop;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface ROPConnector {
+
+    InputStream establishSession() throws IOException;
+
+    InputStream establishSharedSession(String name) throws IOException;
+
+    InputStream sendMessage(byte[] message) throws IOException;
+    
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/http/ClientHessianSerializationServiceProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/http/ClientHessianSerializationServiceProvider.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/ClientHessianSerializationServiceProvider.java
new file mode 100644
index 0000000..ecf9339
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/ClientHessianSerializationServiceProvider.java
@@ -0,0 +1,39 @@
+/*****************************************************************
+ *   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.http;
+
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.remote.hessian.ClientSerializerFactory;
+import org.apache.cayenne.remote.hessian.HessianConfig;
+import org.apache.cayenne.rop.HessianROPSerializationService;
+import org.apache.cayenne.rop.ROPSerializationService;
+
+public class ClientHessianSerializationServiceProvider implements Provider<ROPSerializationService> {
+
+    public static final String[] CLIENT_SERIALIZER_FACTORIES = new String[] {
+            ClientSerializerFactory.class.getName()
+    };
+
+    @Override
+    public ROPSerializationService get() throws DIRuntimeException {
+        return new HessianROPSerializationService(
+                HessianConfig.createFactory(CLIENT_SERIALIZER_FACTORIES, null));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
new file mode 100644
index 0000000..786add2
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
@@ -0,0 +1,233 @@
+/*****************************************************************
+ *   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.http;
+
+import org.apache.cayenne.rop.ROPConnector;
+import org.apache.cayenne.rop.ROPConstants;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+public class HttpROPConnector implements ROPConnector {
+
+    private static Log logger = LogFactory.getLog(HttpROPConnector.class);
+
+    private String url;
+
+    private String username;
+    private String password;
+
+    private Long readTimeout;
+    
+    public HttpROPConnector(String url, String username, String password) {
+        this.url = url;
+        this.username = username;
+        this.password = password;
+    }
+    
+    public void setReadTimeout(Long readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    @Override
+    public InputStream establishSession() throws IOException {
+        if (logger.isInfoEnabled()) {
+            logConnect(null);
+        }
+		
+		Map<String, String> requestParams = new HashMap<>();
+		requestParams.put(ROPConstants.OPERATION_PARAMETER, ROPConstants.ESTABLISH_SESSION_OPERATION);
+		
+        return doRequest(requestParams);
+    }
+
+    @Override
+    public InputStream establishSharedSession(String name) throws IOException {
+        if (logger.isInfoEnabled()) {
+            logConnect(name);
+        }
+
+		Map<String, String> requestParams = new HashMap<>();
+		requestParams.put(ROPConstants.OPERATION_PARAMETER, ROPConstants.ESTABLISH_SHARED_SESSION_OPERATION);
+		requestParams.put(ROPConstants.SESSION_NAME_PARAMETER, name);
+
+		return doRequest(requestParams);
+    }
+
+    @Override
+    public InputStream sendMessage(byte[] message) throws IOException {
+        return doRequest(message);
+    }
+	
+	protected InputStream doRequest(Map<String, String> params) throws IOException {
+		URLConnection connection = new URL(url).openConnection();
+
+		StringBuilder urlParams = new StringBuilder();
+
+		for (Map.Entry<String, String> entry : params.entrySet()) {
+			if (urlParams.length() > 0) {
+				urlParams.append('&');
+			}
+
+			urlParams.append(entry.getKey());
+			urlParams.append('=');
+			urlParams.append(entry.getValue());
+		}
+
+		if (readTimeout != null) {
+			connection.setReadTimeout(readTimeout.intValue());
+		}
+
+		addAuthHeader(connection);
+
+		connection.setDoOutput(true);
+		
+		connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+		connection.setRequestProperty("charset", "utf-8");
+
+		try (OutputStream output = connection.getOutputStream()) {
+			output.write(urlParams.toString().getBytes(StandardCharsets.UTF_8));
+		}
+
+		return connection.getInputStream();
+	} 
+
+    protected InputStream doRequest(byte[] data) throws IOException {
+        URLConnection connection = new URL(url).openConnection();
+
+        if (readTimeout != null) {
+            connection.setReadTimeout(readTimeout.intValue());
+        }
+
+        addAuthHeader(connection);
+        connection.setDoOutput(true);
+
+        if (data != null) {
+            try (OutputStream output = connection.getOutputStream()) {
+                output.write(data);
+            }
+        }
+
+        return connection.getInputStream();
+    }
+
+    protected void addAuthHeader(URLConnection connection) {
+        String basicAuth = getBasicAuth(username, password);
+
+        if (basicAuth != null) {
+            connection.addRequestProperty("Authorization", basicAuth);
+        }
+    }
+
+    public String getBasicAuth(String user, String password) {
+        if (user != null && password != null) {
+            return "Basic " + base64(user + ":" + password);
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates the Base64 value.
+     */
+    private String base64(String value) {
+        StringBuffer cb = new StringBuffer();
+
+        int i = 0;
+        for (i = 0; i + 2 < value.length(); i += 3) {
+            long chunk = (int) value.charAt(i);
+            chunk = (chunk << 8) + (int) value.charAt(i + 1);
+            chunk = (chunk << 8) + (int) value.charAt(i + 2);
+
+            cb.append(encode(chunk >> 18));
+            cb.append(encode(chunk >> 12));
+            cb.append(encode(chunk >> 6));
+            cb.append(encode(chunk));
+        }
+
+        if (i + 1 < value.length()) {
+            long chunk = (int) value.charAt(i);
+            chunk = (chunk << 8) + (int) value.charAt(i + 1);
+            chunk <<= 8;
+
+            cb.append(encode(chunk >> 18));
+            cb.append(encode(chunk >> 12));
+            cb.append(encode(chunk >> 6));
+            cb.append('=');
+        }
+        else if (i < value.length()) {
+            long chunk = (int) value.charAt(i);
+            chunk <<= 16;
+
+            cb.append(encode(chunk >> 18));
+            cb.append(encode(chunk >> 12));
+            cb.append('=');
+            cb.append('=');
+        }
+
+        return cb.toString();
+    }
+
+    public static char encode(long d) {
+        d &= 0x3f;
+        if (d < 26)
+            return (char) (d + 'A');
+        else if (d < 52)
+            return (char) (d + 'a' - 26);
+        else if (d < 62)
+            return (char) (d + '0' - 52);
+        else if (d == 62)
+            return '+';
+        else
+            return '/';
+    }
+
+    private void logConnect(String sharedSessionName) {
+        StringBuilder log = new StringBuilder("Connecting to [");
+        if (username != null) {
+            log.append(username);
+
+            if (password != null) {
+                log.append(":*******");
+            }
+
+            log.append("@");
+        }
+
+        log.append(url);
+        log.append("]");
+
+        if (sharedSessionName != null) {
+            log.append(" - shared session '").append(sharedSessionName).append("'");
+        }
+        else {
+            log.append(" - dedicated session.");
+        }
+
+        logger.info(log.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnectorProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnectorProvider.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnectorProvider.java
new file mode 100644
index 0000000..919a51f
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnectorProvider.java
@@ -0,0 +1,58 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.rop.http;
+
+import org.apache.cayenne.ConfigurationException;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.rop.ROPConnector;
+
+public class HttpROPConnectorProvider implements Provider<ROPConnector> {
+
+    @Inject
+    protected RuntimeProperties runtimeProperties;
+
+    @Override
+    public ROPConnector get() throws DIRuntimeException {
+        String url = runtimeProperties.get(Constants.ROP_SERVICE_URL_PROPERTY);
+        if (url == null) {
+            throw new ConfigurationException(
+                    "No property defined for '%s', can't initialize HessianConnection",
+                    Constants.ROP_SERVICE_URL_PROPERTY);
+        }
+
+        String userName = runtimeProperties.get(Constants.ROP_SERVICE_USERNAME_PROPERTY);
+        String password = runtimeProperties.get(Constants.ROP_SERVICE_PASSWORD_PROPERTY);
+
+        long readTimeout = runtimeProperties.getLong(
+                Constants.ROP_SERVICE_TIMEOUT_PROPERTY,
+                -1L);
+
+        HttpROPConnector result = new HttpROPConnector(url, userName, password);
+
+        if (readTimeout > 0) {
+            result.setReadTimeout(readTimeout);
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientModuleTest.java
----------------------------------------------------------------------
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientModuleTest.java b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientModuleTest.java
index 03d0ad6..836507b 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientModuleTest.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/configuration/rop/client/ClientModuleTest.java
@@ -28,7 +28,7 @@ import org.apache.cayenne.event.DefaultEventManager;
 import org.apache.cayenne.remote.ClientChannel;
 import org.apache.cayenne.remote.ClientConnection;
 import org.apache.cayenne.remote.MockClientConnection;
-import org.apache.cayenne.remote.hessian.HessianConnection;
+import org.apache.cayenne.rop.DefaultClientConnection;
 import org.junit.Test;
 
 import java.util.HashMap;
@@ -52,7 +52,7 @@ public class ClientModuleTest {
 
         ClientConnection connection = injector.getInstance(ClientConnection.class);
         assertNotNull(connection);
-        assertTrue(connection instanceof HessianConnection);
+        assertTrue(connection instanceof DefaultClientConnection);
 
         assertSame("Connection must be a singleton", connection, injector
                 .getInstance(ClientConnection.class));

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
index 6c9a7ea..ceefd62 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/rop/server/ROPServerModule.java
@@ -25,7 +25,9 @@ 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.remote.hessian.service.HessianService;
+import org.apache.cayenne.rop.ServerHessianSerializationServiceProvider;
+import org.apache.cayenne.rop.ServerHttpRemoteService;
+import org.apache.cayenne.rop.ROPSerializationService;
 
 /**
  * A DI module that defines services for the server-side of an ROP application based on
@@ -47,7 +49,8 @@ public class ROPServerModule implements Module {
                 .bindMap(Constants.SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP);
         mapBuilder.putAll(eventBridgeProperties);
 
-        binder.bind(RemoteService.class).to(HessianService.class);
+        binder.bind(RemoteService.class).to(ServerHttpRemoteService.class);
+		binder.bind(ROPSerializationService.class).toProvider(ServerHessianSerializationServiceProvider.class);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
index 3097e36e..3310cee 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
@@ -34,7 +34,7 @@ import com.caucho.hessian.io.Serializer;
  * 
  * @since 1.2
  */
-class ServerSerializerFactory extends AbstractSerializerFactory {
+public class ServerSerializerFactory extends AbstractSerializerFactory {
     private ServerPersistentObjectListSerializer persistentObjectListSerializer;
     private ServerDataRowSerializer dataRowSerilaizer;
     private Serializer javaSerializer;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/HessianROPSerializationService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/HessianROPSerializationService.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/HessianROPSerializationService.java
new file mode 100644
index 0000000..5b0c9a0
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/HessianROPSerializationService.java
@@ -0,0 +1,69 @@
+/*****************************************************************
+ *   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;
+
+import com.caucho.hessian.io.Hessian2Output;
+import com.caucho.hessian.io.HessianInput;
+import com.caucho.hessian.io.HessianOutput;
+import com.caucho.hessian.io.SerializerFactory;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.remote.hessian.HessianConfig;
+import org.apache.cayenne.remote.hessian.service.HessianService;
+
+import java.io.*;
+
+public class HessianROPSerializationService implements ROPSerializationService {
+
+    protected SerializerFactory serializerFactory;
+    
+    public HessianROPSerializationService(SerializerFactory serializerFactory) {
+        this.serializerFactory = serializerFactory;
+    }
+    
+    @Override
+    public byte[] serialize(Object object) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        HessianOutput out = new HessianOutput(bytes);
+        out.setSerializerFactory(serializerFactory);
+        out.writeObject(object);
+
+        return bytes.toByteArray();
+    }
+
+	@Override
+	public void serialize(Object object, OutputStream outputStream) throws IOException {
+		Hessian2Output out = new Hessian2Output(outputStream);
+		out.setSerializerFactory(serializerFactory);
+		out.writeObject(object);
+	}
+
+	@Override
+	public <T> T deserialize(byte[] serializedObject, Class<T> objectClass) {
+		return null;
+	}
+
+	@Override
+    public <T> T deserialize(InputStream input, Class<T> objectClass) throws IOException {
+        HessianInput in = new HessianInput(input);
+        in.setSerializerFactory(serializerFactory);
+
+        return objectClass.cast(in.readObject());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPConstants.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPConstants.java
new file mode 100644
index 0000000..e076eee
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPConstants.java
@@ -0,0 +1,28 @@
+/*****************************************************************
+ *   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;
+
+public class ROPConstants {
+
+    public static final String OPERATION_PARAMETER = "operation";
+    public static final String SESSION_NAME_PARAMETER = "session_name";
+
+    public static final String ESTABLISH_SESSION_OPERATION = "establish_session";
+    public static final String ESTABLISH_SHARED_SESSION_OPERATION = "establish_shared_session";
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPSerializationService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPSerializationService.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPSerializationService.java
new file mode 100644
index 0000000..18c8cdb
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPSerializationService.java
@@ -0,0 +1,34 @@
+/*****************************************************************
+ *   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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public interface ROPSerializationService {
+    
+    byte[] serialize(Object object) throws IOException;
+	
+	void serialize(Object object, OutputStream outputStream) throws IOException;
+    
+    <T> T deserialize(InputStream inputStream, Class<T> objectClass) throws IOException;
+	
+	<T> T deserialize(byte[] serializedObject, Class<T> objectClass) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
new file mode 100644
index 0000000..db8d5fb
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/ROPServlet.java
@@ -0,0 +1,122 @@
+/*****************************************************************
+ *   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;
+
+import org.apache.cayenne.configuration.CayenneRuntime;
+import org.apache.cayenne.configuration.rop.server.ROPServerModule;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+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;
+import org.apache.cayenne.remote.RemoteSession;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+public class ROPServlet extends HttpServlet {
+
+    protected ServletContext servletContext;
+	protected RemoteService remoteService;
+    protected ROPSerializationService serializationService;
+
+    @Override
+    public void init(ServletConfig configuration) throws ServletException {
+
+        checkAlreadyConfigured(configuration.getServletContext());
+
+        this.servletContext = configuration.getServletContext();
+
+        WebConfiguration configAdapter = new WebConfiguration(configuration);
+
+        String configurationLocation = configAdapter.getConfigurationLocation();
+        Map<String, String> eventBridgeParameters = configAdapter.getOtherParameters();
+
+        Collection<Module> modules = configAdapter.createModules(new ROPServerModule(
+                eventBridgeParameters));
+
+        ServerRuntime runtime = new ServerRuntime(configurationLocation, modules
+                .toArray(new Module[modules.size()]));
+
+        this.remoteService = runtime.getInjector().getInstance(RemoteService.class);
+        this.serializationService = runtime.getInjector().getInstance(ROPSerializationService.class);
+
+        WebUtil.setCayenneRuntime(servletContext, runtime);
+        super.init(configuration);
+    }
+
+    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");
+        }
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+
+        CayenneRuntime runtime = WebUtil.getCayenneRuntime(servletContext);
+        if (runtime != null) {
+            runtime.shutdown();
+        }
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+		try {
+			String operation = req.getParameter(ROPConstants.OPERATION_PARAMETER);
+
+			if (operation != null) {
+				switch (operation) {
+					case ROPConstants.ESTABLISH_SESSION_OPERATION:
+						RemoteSession session = remoteService.establishSession();
+						serializationService.serialize(session, resp.getOutputStream());
+						break;
+					case ROPConstants.ESTABLISH_SHARED_SESSION_OPERATION:
+						String sessionName = req.getParameter(ROPConstants.SESSION_NAME_PARAMETER);
+						RemoteSession sharedSession = remoteService.establishSharedSession(sessionName);
+
+						serializationService.serialize(sharedSession, resp.getOutputStream());
+						break;
+					default:
+						throw new ServletException("Unknown operation: " + operation);
+				}
+			} else {
+					Object response = remoteService.processMessage(
+							serializationService.deserialize(req.getInputStream(), ClientMessage.class));
+
+					serializationService.serialize(response, resp.getOutputStream());
+			}
+		} catch (RuntimeException | ServletException e) {
+			throw e;
+		} catch (Throwable e) {
+			throw new ServletException(e);
+		}
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHessianSerializationServiceProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHessianSerializationServiceProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHessianSerializationServiceProvider.java
new file mode 100644
index 0000000..ec0c21c
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHessianSerializationServiceProvider.java
@@ -0,0 +1,37 @@
+/*****************************************************************
+ *   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;
+
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Provider;
+import org.apache.cayenne.remote.hessian.HessianConfig;
+import org.apache.cayenne.remote.hessian.service.ServerSerializerFactory;
+
+public class ServerHessianSerializationServiceProvider implements Provider<ROPSerializationService> {
+
+    public static final String[] SERVER_SERIALIZER_FACTORIES = new String[] {
+            ServerSerializerFactory.class.getName()
+    };
+
+    @Override
+    public ROPSerializationService get() throws DIRuntimeException {
+        return new HessianROPSerializationService(
+                HessianConfig.createFactory(SERVER_SERIALIZER_FACTORIES, null));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/83185f25/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHttpRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHttpRemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHttpRemoteService.java
new file mode 100644
index 0000000..22aba9f
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/rop/ServerHttpRemoteService.java
@@ -0,0 +1,48 @@
+/*****************************************************************
+ *   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;
+
+import com.caucho.services.server.ServiceContext;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.remote.service.HttpRemoteService;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+
+public class ServerHttpRemoteService extends HttpRemoteService {
+	
+	public ServerHttpRemoteService(@Inject ObjectContextFactory contextFactory,
+								   @Inject(Constants.SERVER_ROP_EVENT_BRIDGE_PROPERTIES_MAP) Map<String, String> eventBridgeProperties) {
+		super(contextFactory, eventBridgeProperties);
+	}
+
+	@Override
+	protected HttpSession getSession(boolean create) {
+		HttpServletRequest request = (HttpServletRequest) ServiceContext.getContextRequest();
+		if (request == null) {
+			throw new IllegalStateException(
+					"Attempt to access HttpSession outside the request scope.");
+		}
+
+		return request.getSession(create);
+	}
+}