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/12 13:05:43 UTC
[1/5] cayenne git commit: CAY-2373 cayenne-rop-server module - move
org.apache.cayenne.remote package to cayenne-rop server module - remove
dependencies from cayenne-server pom.xml - update tutorial
Repository: cayenne
Updated Branches:
refs/heads/master eec08b7ef -> 38f37d79a
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
deleted file mode 100644
index 00feca6..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
+++ /dev/null
@@ -1,142 +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.remote.service;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ObjectContextFactory;
-import org.apache.cayenne.event.MockEventBridgeFactory;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.remote.QueryMessage;
-import org.apache.cayenne.remote.RemoteSession;
-import org.apache.cayenne.util.Util;
-import org.junit.Test;
-
-public class BaseRemoteServiceTest {
-
- @Test
- public void testConstructor() throws Exception {
-
- Map<String, String> map = new HashMap<>();
- map.put(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY, MockEventBridgeFactory.class.getName());
-
- ObjectContextFactory factory = new ObjectContextFactory() {
-
- public ObjectContext createContext(DataChannel parent) {
- return null;
- }
-
- public ObjectContext createContext() {
- return null;
- }
- };
- BaseRemoteService service = new BaseRemoteService(factory, map) {
-
- @Override
- protected ServerSession createServerSession() {
- return null;
- }
-
- @Override
- protected ServerSession createServerSession(String name) {
- return null;
- }
-
- @Override
- protected ServerSession getServerSession() {
- return null;
- }
- };
- assertEquals(MockEventBridgeFactory.class.getName(), service.getEventBridgeFactoryName());
- assertSame(factory, service.contextFactory);
-
- }
-
- @Test
- public void testProcessMessageExceptionSerializability() throws Throwable {
-
- Map<String, String> map = new HashMap<>();
- ObjectContextFactory factory = new ObjectContextFactory() {
-
- public ObjectContext createContext(DataChannel parent) {
- return null;
- }
-
- public ObjectContext createContext() {
- return null;
- }
- };
- BaseRemoteService service = new BaseRemoteService(factory, map) {
-
- @Override
- protected ServerSession createServerSession() {
- return new ServerSession(new RemoteSession("a"), null);
- }
-
- @Override
- protected ServerSession createServerSession(String name) {
- return createServerSession();
- }
-
- @Override
- protected ServerSession getServerSession() {
- return createServerSession();
- }
- };
-
- try {
- service.processMessage(new QueryMessage(null) {
-
- @Override
- public Query getQuery() {
- // serializable exception thrown
- throw new CayenneRuntimeException();
- }
- });
-
- fail("Expected to throw");
- } catch (Exception ex) {
- Util.cloneViaSerialization(ex);
- }
-
- try {
- service.processMessage(new QueryMessage(null) {
-
- @Override
- public Query getQuery() {
- // non-serializable exception thrown
- throw new MockUnserializableException();
- }
- });
-
- fail("Expected to throw");
- } catch (Exception ex) {
- Util.cloneViaSerialization(ex);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
deleted file mode 100644
index e4b01fe..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
+++ /dev/null
@@ -1,54 +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.remote.service;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.MockDataChannel;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.remote.BootstrapMessage;
-import org.apache.cayenne.remote.ClientMessage;
-import org.junit.Test;
-
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-
-public class DispatchHelperTest {
-
- @Test
- public void testBootstrapMessage() {
- EntityResolver resolver = new EntityResolver();
- MockDataChannel channel = new MockDataChannel(resolver);
- assertSame(resolver.getClientEntityResolver(), DispatchHelper.dispatch(
- channel,
- new BootstrapMessage()));
- }
-
- @Test
- public void testUnknownMessage() {
- try {
- DispatchHelper.dispatch(new MockDataChannel(), mock(ClientMessage.class));
- fail("Unknown message must have failed");
- }
- catch (CayenneRuntimeException e) {
- // expected
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
deleted file mode 100644
index e30435b..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
+++ /dev/null
@@ -1,24 +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.remote.service;
-
-class MockUnserializableException extends RuntimeException {
-
- protected Object notSerializableField = new Object();
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
----------------------------------------------------------------------
diff --git a/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java b/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
index e2434ce..d70843c 100644
--- a/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
+++ b/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
@@ -32,7 +32,7 @@ public class Main {
public static void main(String[] args) {
Map<String, String> properties = new HashMap<>();
- properties.put(ClientConstants.ROP_SERVICE_URL_PROPERTY, "http://localhost:8080/tutorial-rop-server/cayenne-service");
+ properties.put(ClientConstants.ROP_SERVICE_URL_PROPERTY, "http://localhost:8080/cayenne-service");
properties.put(ClientConstants.ROP_SERVICE_USERNAME_PROPERTY, "cayenne-user");
properties.put(ClientConstants.ROP_SERVICE_PASSWORD_PROPERTY, "secret");
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/tutorials/tutorial-rop-server/pom.xml
----------------------------------------------------------------------
diff --git a/tutorials/tutorial-rop-server/pom.xml b/tutorials/tutorial-rop-server/pom.xml
index c234bd2..4bde8ec 100644
--- a/tutorials/tutorial-rop-server/pom.xml
+++ b/tutorials/tutorial-rop-server/pom.xml
@@ -28,12 +28,13 @@
<dependencies>
<dependency>
<groupId>org.apache.cayenne</groupId>
- <artifactId>cayenne-server</artifactId>
+ <artifactId>cayenne-rop-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
+ <scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/tutorials/tutorial-rop-server/src/main/resources/cayenne-project.xml
----------------------------------------------------------------------
diff --git a/tutorials/tutorial-rop-server/src/main/resources/cayenne-project.xml b/tutorials/tutorial-rop-server/src/main/resources/cayenne-project.xml
index 1c7f367..27b1c1b 100644
--- a/tutorials/tutorial-rop-server/src/main/resources/cayenne-project.xml
+++ b/tutorials/tutorial-rop-server/src/main/resources/cayenne-project.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<domain project-version="9">
+<domain xmlns="http://cayenne.apache.org/schema/10/domain"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd"
+ project-version="10">
<map name="datamap"/>
-
<node name="datanode"
factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
- schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy"
- >
+ schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy">
<map-ref name="datamap"/>
<data-source>
<driver value="org.apache.derby.jdbc.EmbeddedDriver"/>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/tutorials/tutorial-rop-server/src/main/resources/datamap.map.xml
----------------------------------------------------------------------
diff --git a/tutorials/tutorial-rop-server/src/main/resources/datamap.map.xml b/tutorials/tutorial-rop-server/src/main/resources/datamap.map.xml
index e0a7e3f..f879288 100644
--- a/tutorials/tutorial-rop-server/src/main/resources/datamap.map.xml
+++ b/tutorials/tutorial-rop-server/src/main/resources/datamap.map.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<data-map xmlns="http://cayenne.apache.org/schema/9/modelMap"
+<data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://cayenne.apache.org/schema/9/modelMap http://cayenne.apache.org/schema/9/modelMap.xsd"
- project-version="9">
+ xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap http://cayenne.apache.org/schema/10/modelMap.xsd"
+ project-version="10">
<property name="defaultPackage" value="org.apache.cayenne.tutorial.persistent"/>
<property name="clientSupported" value="true"/>
<property name="defaultClientPackage" value="org.apache.cayenne.tutorial.persistent.client"/>
@@ -37,10 +37,10 @@
<db-relationship name="paintings" source="GALLERY" target="PAINTING" toMany="true">
<db-attribute-pair source="ID" target="GALLERY_ID"/>
</db-relationship>
- <db-relationship name="artist" source="PAINTING" target="ARTIST" toMany="false">
+ <db-relationship name="artist" source="PAINTING" target="ARTIST">
<db-attribute-pair source="ARTIST_ID" target="ID"/>
</db-relationship>
- <db-relationship name="gallery" source="PAINTING" target="GALLERY" toMany="false">
+ <db-relationship name="gallery" source="PAINTING" target="GALLERY">
<db-attribute-pair source="GALLERY_ID" target="ID"/>
</db-relationship>
<obj-relationship name="paintings" source="Artist" target="Painting" deleteRule="Cascade" db-relationship-path="paintings"/>
[4/5] cayenne git commit: CAY-2373 cayenne-rop-server module - move
org.apache.cayenne.remote package to cayenne-rop server module - remove
dependencies from cayenne-server pom.xml - update tutorial
Posted by nt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
new file mode 100644
index 0000000..7ed1004
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
@@ -0,0 +1,668 @@
+/*****************************************************************
+ * 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.remote;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.util.IDUtil;
+import org.apache.cayenne.util.IncrementalListResponse;
+import org.apache.cayenne.util.Util;
+
+/**
+ * A list that serves as a container of Persistent objects. It is usually returned by an
+ * ObjectContext when a paginated query is performed. Initially only the first "page" of
+ * objects is fully resolved. Pages following the first page are resolved on demand. When
+ * a list element is accessed, the list would ensure that this element as well as all its
+ * siblings on the same page are fully resolved.
+ * <p>
+ * The list can hold DataRows or Persistent objects. Attempts to add any other object
+ * types will result in an exception.
+ * </p>
+ * <p>
+ * Certain operations like <code>toArray</code> would trigger full list fetch.
+ * </p>
+ * <p>
+ * Synchronization Note: this list is not synchronized. All access to it should follow
+ * synchronization rules applicable for ArrayList.
+ * </p>
+ *
+ * @since 1.2
+ */
+public class RemoteIncrementalFaultList implements List {
+
+ static final Object PLACEHOLDER = new Object();
+
+ protected List elements;
+
+ protected String cacheKey;
+ protected int pageSize;
+ protected int unfetchedObjects;
+ protected Query paginatedQuery;
+
+ protected transient ObjectContext context;
+
+ /**
+ * Stores a hint allowing to distinguish data rows from unfetched ids when the query
+ * fetches data rows.
+ */
+ protected int rowWidth;
+
+ private ListHelper helper;
+
+ public RemoteIncrementalFaultList(ObjectContext context, Query paginatedQuery) {
+
+ QueryMetadata metadata = paginatedQuery.getMetaData(context.getEntityResolver());
+
+ if (metadata.getPageSize() <= 0) {
+ throw new IllegalArgumentException("Page size must be positive: "
+ + metadata.getPageSize());
+ }
+
+ this.pageSize = metadata.getPageSize();
+ this.helper = (metadata.isFetchingDataRows())
+ ? new DataRowListHelper()
+ : new PersistentListHelper();
+ this.context = context;
+
+ // use provided cache key if possible; this would allow clients to
+ // address the same server-side list from multiple queries.
+ this.cacheKey = metadata.getCacheKey();
+ if (cacheKey == null) {
+ cacheKey = generateCacheKey();
+ }
+
+ Query query = paginatedQuery;
+
+ // always wrap a query in a Incremental*Query, to ensure cache key is
+ // client-generated (e.g. see CAY-1003 - client and server can be in different
+ // timezones, so the key can be messed up)
+
+ // there are some serious pagination optimizations for SelectQuery on the
+ // server-side, so use a special wrapper that is itself a subclass of
+ // SelectQuery
+ if (query instanceof SelectQuery) {
+ query = new IncrementalSelectQuery<Object>((SelectQuery<Object>) paginatedQuery, cacheKey);
+ }
+ else {
+ query = new IncrementalQuery(paginatedQuery, cacheKey);
+ }
+
+ // ensure that originating query is wrapped to include the right cache key....
+ this.paginatedQuery = query;
+
+ // select directly from the channel, bypassing the context. Otherwise our query
+ // wrapper can be intercepted incorrectly
+ QueryResponse response = context.getChannel().onQuery(context, query);
+
+ List firstPage = response.firstList();
+
+ // sanity check
+ if (firstPage.size() > pageSize) {
+ throw new IllegalArgumentException("Returned page size ("
+ + firstPage.size()
+ + ") exceeds requested page size ("
+ + pageSize
+ + ")");
+ }
+ // result is smaller than a page
+ else if (firstPage.size() < pageSize) {
+ this.elements = new ArrayList(firstPage);
+ unfetchedObjects = 0;
+ }
+ else {
+
+ if (response instanceof IncrementalListResponse) {
+ int fullListSize = ((IncrementalListResponse) response).getFullSize();
+
+ this.unfetchedObjects = fullListSize - firstPage.size();
+ this.elements = new ArrayList(fullListSize);
+ elements.addAll(firstPage);
+
+ // fill the rest with placeholder...
+ for (int i = pageSize; i < fullListSize; i++) {
+ elements.add(PLACEHOLDER);
+ }
+ }
+ // this happens when full size equals page size
+ else {
+ this.elements = new ArrayList(firstPage);
+ unfetchedObjects = 0;
+ }
+ }
+ }
+
+ private String generateCacheKey() {
+ byte[] bytes = IDUtil.pseudoUniqueByteSequence8();
+ StringBuffer buffer = new StringBuffer(17);
+ buffer.append("I");
+ for (byte aByte : bytes) {
+ IDUtil.appendFormattedByte(buffer, aByte);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Will resolve all unread objects.
+ */
+ public void resolveAll() {
+ resolveInterval(0, size());
+ }
+
+ /**
+ * @param object
+ * @return <code>true</code> if the object corresponds to an unresolved state and
+ * does require a fetch before being returned to the user.
+ */
+ private boolean isUnresolved(Object object) {
+ return object == PLACEHOLDER;
+ }
+
+ /**
+ * Resolves a sublist of objects starting at <code>fromIndex</code> up to but not
+ * including <code>toIndex</code>. Internally performs bound checking and trims
+ * indexes accordingly.
+ */
+ protected void resolveInterval(int fromIndex, int toIndex) {
+ if (fromIndex >= toIndex || elements.isEmpty()) {
+ return;
+ }
+
+ if (context == null) {
+ throw new CayenneRuntimeException("No ObjectContext set, can't resolve objects.");
+ }
+
+ // bounds checking
+
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+
+ if (toIndex > elements.size()) {
+ toIndex = elements.size();
+ }
+
+ // find disjoint ranges and resolve them individually...
+
+ int fromPage = pageIndex(fromIndex);
+ int toPage = pageIndex(toIndex - 1);
+
+ int rangeStartIndex = -1;
+ for (int i = fromPage; i <= toPage; i++) {
+
+ int pageStartIndex = i * pageSize;
+ Object firstPageObject = elements.get(pageStartIndex);
+ if (isUnresolved(firstPageObject)) {
+
+ // start range
+ if (rangeStartIndex < 0) {
+ rangeStartIndex = pageStartIndex;
+ }
+ }
+ else {
+
+ // finish range...
+ if (rangeStartIndex >= 0) {
+ forceResolveInterval(rangeStartIndex, pageStartIndex);
+ rangeStartIndex = -1;
+ }
+ }
+ }
+
+ // load last page
+ if (rangeStartIndex >= 0) {
+ forceResolveInterval(rangeStartIndex, toIndex);
+ }
+ }
+
+ void forceResolveInterval(int fromIndex, int toIndex) {
+
+ int pastEnd = toIndex - size();
+ if (pastEnd > 0) {
+ toIndex = size();
+ }
+
+ int fetchLimit = toIndex - fromIndex;
+
+ RangeQuery query = new RangeQuery(cacheKey, fromIndex, fetchLimit, paginatedQuery);
+
+ List sublist = context.performQuery(query);
+
+ // sanity check
+ if (sublist.size() != fetchLimit) {
+ throw new CayenneRuntimeException("Resolved range size %d is not the same as expected: %d"
+ , sublist.size(), fetchLimit);
+ }
+
+ for (int i = 0; i < fetchLimit; i++) {
+ elements.set(fromIndex + i, sublist.get(i));
+ }
+
+ unfetchedObjects -= sublist.size();
+ }
+
+ /**
+ * Returns zero-based index of the virtual "page" for a given array element index.
+ */
+ int pageIndex(int elementIndex) {
+ if (elementIndex < 0 || elementIndex > size()) {
+ throw new IndexOutOfBoundsException("Index: " + elementIndex);
+ }
+
+ if (pageSize <= 0 || elementIndex < 0) {
+ return -1;
+ }
+
+ return elementIndex / pageSize;
+ }
+
+ /**
+ * Returns ObjectContext associated with this list.
+ */
+ public ObjectContext getContext() {
+ return context;
+ }
+
+ /**
+ * Returns the pageSize.
+ *
+ * @return int
+ */
+ public int getPageSize() {
+ return pageSize;
+ }
+
+ /**
+ * Returns a list iterator for this list. DataObjects are resolved a page (according
+ * to getPageSize()) at a time as necessary - when retrieved with next() or
+ * previous().
+ */
+ public ListIterator listIterator() {
+ return new ListIteratorHelper(0);
+ }
+
+ /**
+ * Returns a list iterator of the elements in this list (in proper sequence), starting
+ * at the specified position in this list. The specified index indicates the first
+ * element that would be returned by an initial call to the next method. An initial
+ * call to the previous method would return the element with the specified index minus
+ * one. DataObjects are resolved a page at a time (according to getPageSize()) as
+ * necessary - when retrieved with next() or previous().
+ */
+ public ListIterator listIterator(int index) {
+ if (index < 0 || index > size()) {
+ throw new IndexOutOfBoundsException("Index: " + index);
+ }
+
+ return new ListIteratorHelper(index);
+ }
+
+ /**
+ * Return an iterator for this list. DataObjects are resolved a page (according to
+ * getPageSize()) at a time as necessary - when retrieved with next().
+ */
+ public Iterator iterator() {
+ // by virtue of get(index)'s implementation, resolution of ids into
+ // objects will occur on pageSize boundaries as necessary.
+ return new Iterator() {
+
+ int listIndex = 0;
+
+ public boolean hasNext() {
+ return (listIndex < elements.size());
+ }
+
+ public Object next() {
+ if (listIndex >= elements.size())
+ throw new NoSuchElementException("no more elements");
+
+ return get(listIndex++);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove not supported.");
+ }
+ };
+ }
+
+ /**
+ * @see java.util.List#add(int, Object)
+ */
+ public void add(int index, Object element) {
+ helper.validateListObject(element);
+ elements.add(index, element);
+
+ }
+
+ /**
+ * @see java.util.Collection#add(Object)
+ */
+ public boolean add(Object o) {
+ helper.validateListObject(o);
+ return elements.add(o);
+ }
+
+ /**
+ * @see java.util.Collection#addAll(Collection)
+ */
+ public boolean addAll(Collection c) {
+
+ return elements.addAll(c);
+
+ }
+
+ /**
+ * @see java.util.List#addAll(int, Collection)
+ */
+ public boolean addAll(int index, Collection c) {
+
+ return elements.addAll(index, c);
+
+ }
+
+ /**
+ * @see java.util.Collection#clear()
+ */
+ public void clear() {
+ elements.clear();
+ }
+
+ /**
+ * @see java.util.Collection#contains(Object)
+ */
+ public boolean contains(Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ /**
+ * @see java.util.Collection#containsAll(Collection)
+ */
+ public boolean containsAll(Collection c) {
+ Iterator it = c.iterator();
+ while (it.hasNext()) {
+ if (!contains(it.next())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ public Object get(int index) {
+
+ Object o = elements.get(index);
+
+ if (isUnresolved(o)) {
+ // read this page
+ int pageStart = pageIndex(index) * pageSize;
+ resolveInterval(pageStart, pageStart + pageSize);
+
+ return elements.get(index);
+ }
+ else {
+ return o;
+ }
+
+ }
+
+ /**
+ * @see java.util.List#indexOf(Object)
+ */
+ public int indexOf(Object o) {
+ return helper.indexOfObject(o);
+ }
+
+ /**
+ * @see java.util.Collection#isEmpty()
+ */
+ public boolean isEmpty() {
+
+ return elements.isEmpty();
+
+ }
+
+ /**
+ * @see java.util.List#lastIndexOf(Object)
+ */
+ public int lastIndexOf(Object o) {
+ return helper.lastIndexOfObject(o);
+ }
+
+ /**
+ * @see java.util.List#remove(int)
+ */
+ public Object remove(int index) {
+
+ return elements.remove(index);
+
+ }
+
+ /**
+ * @see java.util.Collection#remove(Object)
+ */
+ public boolean remove(Object o) {
+
+ return elements.remove(o);
+
+ }
+
+ /**
+ * @see java.util.Collection#removeAll(Collection)
+ */
+ public boolean removeAll(Collection c) {
+
+ return elements.removeAll(c);
+
+ }
+
+ /**
+ * @see java.util.Collection#retainAll(Collection)
+ */
+ public boolean retainAll(Collection c) {
+
+ return elements.retainAll(c);
+
+ }
+
+ /**
+ * @see java.util.List#set(int, Object)
+ */
+ public Object set(int index, Object element) {
+ helper.validateListObject(element);
+
+ return elements.set(index, element);
+
+ }
+
+ /**
+ * @see java.util.Collection#size()
+ */
+ public int size() {
+ return elements.size();
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ resolveInterval(fromIndex, toIndex);
+ return elements.subList(fromIndex, toIndex);
+ }
+
+ public Object[] toArray() {
+ resolveAll();
+
+ return elements.toArray();
+ }
+
+ /**
+ * @see java.util.Collection#toArray(Object[])
+ */
+ public Object[] toArray(Object[] a) {
+ resolveAll();
+
+ return elements.toArray(a);
+ }
+
+ /**
+ * Returns a total number of objects that are not resolved yet.
+ */
+ public int getUnfetchedObjects() {
+ return unfetchedObjects;
+ }
+
+ abstract class ListHelper {
+
+ int indexOfObject(Object object) {
+ if (incorrectObjectType(object)) {
+ return -1;
+ }
+
+ for (int i = 0; i < elements.size(); i++) {
+
+ if (Util.nullSafeEquals(object, get(i))) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ int lastIndexOfObject(Object object) {
+ if (incorrectObjectType(object)) {
+ return -1;
+ }
+
+ for (int i = elements.size() - 1; i >= 0; i--) {
+ if (Util.nullSafeEquals(object, get(i))) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ abstract boolean incorrectObjectType(Object object);
+
+ void validateListObject(Object object) throws IllegalArgumentException {
+ if (incorrectObjectType(object)) {
+ throw new IllegalArgumentException("Can't store this object: " + object);
+ }
+ }
+ }
+
+ class PersistentListHelper extends ListHelper {
+
+ @Override
+ boolean incorrectObjectType(Object object) {
+ if (!(object instanceof Persistent)) {
+ return true;
+ }
+
+ Persistent persistent = (Persistent) object;
+ if (persistent.getObjectContext() != context) {
+ return true;
+ }
+
+ return false;
+ }
+
+ }
+
+ class DataRowListHelper extends ListHelper {
+
+ @Override
+ boolean incorrectObjectType(Object object) {
+ if (!(object instanceof Map)) {
+ return true;
+ }
+
+ Map map = (Map) object;
+ return map.size() != rowWidth;
+ }
+ }
+
+ class ListIteratorHelper implements ListIterator {
+
+ // by virtue of get(index)'s implementation, resolution of ids into
+ // objects will occur on pageSize boundaries as necessary.
+
+ int listIndex;
+
+ public ListIteratorHelper(int startIndex) {
+ this.listIndex = startIndex;
+ }
+
+ public void add(Object o) {
+ throw new UnsupportedOperationException("add operation not supported");
+ }
+
+ public boolean hasNext() {
+ return (listIndex < elements.size());
+ }
+
+ public boolean hasPrevious() {
+ return (listIndex > 0);
+ }
+
+ public Object next() {
+ if (listIndex >= elements.size())
+ throw new NoSuchElementException("at the end of the list");
+
+ return get(listIndex++);
+ }
+
+ public int nextIndex() {
+ return listIndex;
+ }
+
+ public Object previous() {
+ if (listIndex < 1)
+ throw new NoSuchElementException("at the beginning of the list");
+
+ return get(--listIndex);
+ }
+
+ public int previousIndex() {
+ return (listIndex - 1);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove operation not supported");
+ }
+
+ public void set(Object o) {
+ throw new UnsupportedOperationException("set operation not supported");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteService.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
new file mode 100644
index 0000000..f357846
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.remote;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Interface of a Cayenne remote service.
+ *
+ * @since 1.2
+ * @see org.apache.cayenne.rop.ROPServlet
+ */
+public interface RemoteService extends Remote {
+
+ /**
+ * Establishes a dedicated session with Cayenne DataChannel, returning session id.
+ */
+ RemoteSession establishSession() throws RemoteException;
+
+ /**
+ * Creates a new session with the specified or joins an existing one. This method is
+ * used to bootstrap collaborating clients of a single "group chat".
+ */
+ RemoteSession establishSharedSession(String name) throws RemoteException;
+
+ /**
+ * Processes message on a remote server, returning the result of such processing.
+ */
+ Object processMessage(ClientMessage message) throws RemoteException, Throwable;
+
+ /**
+ * Close remote service resources.
+ * @sine 4.0
+ */
+ void close() throws RemoteException;
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
new file mode 100644
index 0000000..461e79d
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
@@ -0,0 +1,145 @@
+/*****************************************************************
+ * 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.remote;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.event.EventBridge;
+import org.apache.cayenne.event.EventBridgeFactory;
+import org.apache.cayenne.event.EventSubject;
+import org.apache.cayenne.util.HashCodeBuilder;
+import org.apache.cayenne.util.ToStringBuilder;
+
+/**
+ * A descriptor used by default service implementation to pass session parameters to the
+ * client. It provides the client with details on how to invoke the service and how to
+ * listen for the server events.
+ *
+ * @since 1.2
+ */
+public class RemoteSession implements Serializable {
+
+ static final Collection<EventSubject> SUBJECTS = Arrays.asList(
+ DataChannel.GRAPH_CHANGED_SUBJECT,
+ DataChannel.GRAPH_FLUSHED_SUBJECT,
+ DataChannel.GRAPH_ROLLEDBACK_SUBJECT);
+
+ protected String name;
+ protected String sessionId;
+
+ protected String eventBridgeFactory;
+ protected Map<String, String> eventBridgeParameters;
+
+ // private constructor used by hessian deserialization mechanism
+ @SuppressWarnings("unused")
+ private RemoteSession() {
+
+ }
+
+ /**
+ * Creates a HessianServiceDescriptor without server events support.
+ */
+ public RemoteSession(String sessionId) {
+ this(sessionId, null, null);
+ }
+
+ /**
+ * Creates a HessianServiceDescriptor. If <code>eventBridgeFactory</code> argument
+ * is not null, session will support server events.
+ */
+ public RemoteSession(String sessionId, String eventBridgeFactory,
+ Map<String, String> eventBridgeParameters) {
+
+ if (sessionId == null) {
+ throw new IllegalArgumentException("Null sessionId");
+ }
+
+ this.sessionId = sessionId;
+ this.eventBridgeFactory = eventBridgeFactory;
+ this.eventBridgeParameters = eventBridgeParameters;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(71, 5).append(sessionId).toHashCode();
+ }
+
+ /**
+ * Returns server session id. This is often the same as HttpSession id.
+ */
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ /**
+ * Returns session group name. Group name is used for shared sessions.
+ */
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isServerEventsEnabled() {
+ return eventBridgeFactory != null;
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this)
+ .append("sessionId", sessionId);
+
+ if (eventBridgeFactory != null) {
+ builder.append("eventBridgeFactory", eventBridgeFactory);
+ }
+
+ if (name != null) {
+ builder.append("name", name);
+ }
+
+ return builder.toString();
+ }
+
+ public static Collection<EventSubject> getSubjects() {
+ return SUBJECTS;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public String getEventBridgeFactory() {
+ return eventBridgeFactory;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public Map<String, String> getEventBridgeParameters() {
+ return eventBridgeParameters != null ? eventBridgeParameters : Collections.<String, String> emptyMap();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
new file mode 100644
index 0000000..d8d5f37
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
@@ -0,0 +1,91 @@
+/*****************************************************************
+ * 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.remote;
+
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.graph.GraphDiff;
+
+/**
+ * A message used for synchronization of the child with parent. It defines a few types of
+ * synchronization: "flush" (when the child sends its changes without a commit), "commit"
+ * (cascading flush with ultimate commit to the database), and "rollback" - cascading
+ * reverting of all uncommitted changes.
+ *
+ * @since 1.2
+ */
+public class SyncMessage implements ClientMessage {
+
+ protected transient ObjectContext source;
+ protected int type;
+ protected GraphDiff senderChanges;
+
+ // private constructor for Hessian deserialization
+ @SuppressWarnings("unused")
+ private SyncMessage() {
+
+ }
+
+ public SyncMessage(ObjectContext source, int syncType, GraphDiff senderChanges) {
+ // validate type
+ if (syncType != DataChannel.FLUSH_NOCASCADE_SYNC
+ && syncType != DataChannel.FLUSH_CASCADE_SYNC
+ && syncType != DataChannel.ROLLBACK_CASCADE_SYNC) {
+ throw new IllegalArgumentException("'type' is invalid: " + syncType);
+ }
+
+ this.source = source;
+ this.type = syncType;
+ this.senderChanges = senderChanges;
+ }
+
+ /**
+ * Returns a source of SyncMessage.
+ */
+ public ObjectContext getSource() {
+ return source;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public GraphDiff getSenderChanges() {
+ return senderChanges;
+ }
+
+ /**
+ * Returns a description of the type of message.
+ * Possibilities are "flush-sync", "flush-cascade-sync", "rollback-cascade-sync" or "unknown-sync".
+ */
+ @Override
+ public String toString() {
+ switch (type) {
+ case DataChannel.FLUSH_NOCASCADE_SYNC:
+ return "flush-sync";
+ case DataChannel.FLUSH_CASCADE_SYNC:
+ return "flush-cascade-sync";
+ case DataChannel.ROLLBACK_CASCADE_SYNC:
+ return "rollback-cascade-sync";
+ default:
+ return "unknown-sync";
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
new file mode 100644
index 0000000..2357b2e
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
@@ -0,0 +1,43 @@
+/*****************************************************************
+ * 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.remote.hessian;
+
+import com.caucho.hessian.io.AbstractSerializerFactory;
+import com.caucho.hessian.io.Deserializer;
+import com.caucho.hessian.io.HessianProtocolException;
+
+// This class is an ugly workaround for Hessian 4 bug with not loading custom deserializers.
+// TODO: once it is fixed in Hessian, remove this class
+class CayenneSerializerFactory extends com.caucho.hessian.io.SerializerFactory {
+ @Override
+ public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
+ for (int i = 0; _factories != null && i < _factories.size(); i++) {
+ AbstractSerializerFactory factory;
+ factory = (AbstractSerializerFactory) _factories.get(i);
+
+ Deserializer deserializer = factory.getDeserializer(cl);
+ if (deserializer != null) {
+ return deserializer;
+ }
+ }
+
+ return super.getDeserializer(cl);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
new file mode 100644
index 0000000..550ef09
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
@@ -0,0 +1,114 @@
+/*****************************************************************
+ * 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.remote.hessian;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.util.Util;
+
+import com.caucho.hessian.io.AbstractSerializerFactory;
+import com.caucho.hessian.io.SerializerFactory;
+
+/**
+ * A utility class that configures Hessian serialization properties using reflection.
+ *
+ * @since 1.2
+ */
+public class HessianConfig {
+
+ /**
+ * Creates a Hessian SerializerFactory configured with zero or more
+ * AbstractSerializerFactory extensions. Extensions are specified as class names. This
+ * method can inject EntityResolver if an extension factory class defines
+ * <em>setEntityResolver(EntityResolver)</em> method.
+ *
+ * @param factoryNames an array of factory class names. Each class must be a concrete
+ * subclass of <em>com.caucho.hessian.io.AbstractSerializerFactory</em>
+ * and have a default constructor.
+ * @param resolver if not null, EntityResolver will be injected into all factories
+ * that implement <em>setEntityResolver(EntityResolver)</em> method.
+ */
+ public static SerializerFactory createFactory(
+ String[] factoryNames,
+ EntityResolver resolver) {
+
+ SerializerFactory factory = new CayenneSerializerFactory();
+
+ if (factoryNames != null && factoryNames.length > 0) {
+
+ for (String factoryName : factoryNames) {
+
+ try {
+ factory.addFactory(loadFactory(factoryName, resolver));
+ }
+ catch (Exception e) {
+ throw new CayenneRuntimeException("Error configuring factory class "
+ + factoryName, e);
+ }
+ }
+ }
+
+ return factory;
+ }
+
+ static AbstractSerializerFactory loadFactory(
+ String factoryName,
+ EntityResolver resolver) throws Exception {
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ Class factoryClass = Class.forName(factoryName, true, loader);
+
+ if (!AbstractSerializerFactory.class.isAssignableFrom(factoryClass)) {
+ throw new IllegalArgumentException(factoryClass
+ + " is not a AbstractSerializerFactory");
+ }
+
+ Constructor c = factoryClass.getDeclaredConstructor();
+ if (!Util.isAccessible(c)) {
+ c.setAccessible(true);
+ }
+
+ AbstractSerializerFactory object = (AbstractSerializerFactory) c.newInstance();
+
+ if (resolver != null) {
+ try {
+
+ Method setter = factoryClass.getDeclaredMethod(
+ "setEntityResolver",
+ EntityResolver.class);
+
+ if (!Util.isAccessible(setter)) {
+ setter.setAccessible(true);
+ }
+
+ setter.invoke(object, resolver);
+ }
+ catch (Exception e) {
+ // ignore injection exception
+ }
+ }
+
+ return object;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
new file mode 100644
index 0000000..5409e43
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ * 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.remote.hessian.service;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+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 com.caucho.services.server.ServiceContext;
+
+/**
+ * An implementation of RemoteService for work within Caucho Hessian environment.
+ *
+ * @since 3.1 the service API is reworked to initialize via Cayenne DI.
+ */
+public class HessianService extends HttpRemoteService {
+
+ public static final String[] SERVER_SERIALIZER_FACTORIES = new String[] {
+ ServerSerializerFactory.class.getName()
+ };
+
+ /**
+ * @since 3.1
+ */
+ public HessianService(@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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
new file mode 100644
index 0000000..bcd9f52
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
@@ -0,0 +1,56 @@
+/*****************************************************************
+ * 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.remote.hessian.service;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.cayenne.DataRow;
+
+import com.caucho.hessian.io.AbstractHessianOutput;
+import com.caucho.hessian.io.AbstractSerializer;
+
+/**
+ * A server-side DataRow Hessian serializer.
+ */
+class ServerDataRowSerializer extends AbstractSerializer {
+
+ @Override
+ public void writeObject(Object object, AbstractHessianOutput out) throws IOException {
+ if (out.addRef(object)) {
+ return;
+ }
+
+ DataRow row = (DataRow) object;
+
+ out.writeMapBegin(DataRow.class.getName());
+
+ out.writeInt(row.size());
+ out.writeLong(row.getVersion());
+ out.writeLong(row.getReplacesVersion());
+
+ for (final Map.Entry<String, Object> entry : row.entrySet()) {
+ out.writeObject(entry.getKey());
+ out.writeObject(entry.getValue());
+ }
+
+ out.writeMapEnd();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
new file mode 100644
index 0000000..cf31ef0
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ * 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.remote.hessian.service;
+
+import java.io.IOException;
+
+import org.apache.cayenne.util.PersistentObjectList;
+
+import com.caucho.hessian.io.AbstractHessianOutput;
+import com.caucho.hessian.io.JavaSerializer;
+
+/**
+ * Serializer for PersistentObjectLists.
+ *
+ * @since 1.2
+ */
+class ServerPersistentObjectListSerializer extends JavaSerializer {
+
+ ServerPersistentObjectListSerializer() {
+ super(PersistentObjectList.class);
+ }
+
+ @Override
+ public void writeObject(Object object, AbstractHessianOutput out) throws IOException {
+ PersistentObjectList list = (PersistentObjectList) object;
+ if (list.isFault()) {
+ out.writeNull();
+ }
+ else {
+ super.writeObject(object, out);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
new file mode 100644
index 0000000..3310cee
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ * 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.remote.hessian.service;
+
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.util.PersistentObjectList;
+import org.apache.cayenne.util.PersistentObjectMap;
+
+import com.caucho.hessian.io.AbstractSerializerFactory;
+import com.caucho.hessian.io.Deserializer;
+import com.caucho.hessian.io.HessianProtocolException;
+import com.caucho.hessian.io.JavaSerializer;
+import com.caucho.hessian.io.Serializer;
+
+/**
+ * An object that manages all custom (de)serializers used on the server.
+ *
+ * @since 1.2
+ */
+public class ServerSerializerFactory extends AbstractSerializerFactory {
+ private ServerPersistentObjectListSerializer persistentObjectListSerializer;
+ private ServerDataRowSerializer dataRowSerilaizer;
+ private Serializer javaSerializer;
+
+ ServerSerializerFactory() {
+ this.persistentObjectListSerializer = new ServerPersistentObjectListSerializer();
+ this.dataRowSerilaizer = new ServerDataRowSerializer();
+ }
+
+ @Override
+ public Serializer getSerializer(Class cl) throws HessianProtocolException {
+
+ if (PersistentObjectList.class.isAssignableFrom(cl)) {
+ return persistentObjectListSerializer;
+ }
+ else if (DataRow.class.isAssignableFrom(cl)) {
+ return dataRowSerilaizer;
+ }
+ //turns out Hessian uses its own (incorrect) serialization mechanism for maps
+ else if (PersistentObjectMap.class.isAssignableFrom(cl)) {
+ if (javaSerializer == null) {
+ javaSerializer = new JavaSerializer(cl);
+ }
+ return javaSerializer;
+ }
+
+ return null;
+ }
+
+ @Override
+ public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
new file mode 100644
index 0000000..68fd89d
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
@@ -0,0 +1,199 @@
+/*****************************************************************
+ * 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.remote.service;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.access.ClientServerChannel;
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.remote.ClientMessage;
+import org.apache.cayenne.remote.RemoteService;
+import org.apache.cayenne.remote.RemoteSession;
+import org.apache.cayenne.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.rmi.RemoteException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A generic implementation of an RemoteService. Can be subclassed to work with
+ * different remoting mechanisms, such as Hessian or JAXRPC.
+ *
+ * @since 1.2
+ */
+public abstract class BaseRemoteService implements RemoteService {
+
+ // keep logger non-static so that it could be garbage collected with this
+ // instance.
+ protected final Logger logger;
+
+ protected ObjectContextFactory contextFactory;
+ protected String eventBridgeFactoryName;
+ protected Map<String, String> eventBridgeParameters;
+
+ /**
+ * @since 3.1
+ */
+ public BaseRemoteService(ObjectContextFactory contextFactory, Map<String, String> eventBridgeProperties) {
+
+ logger = LoggerFactory.getLogger(getClass());
+
+ // start Cayenne service
+ logger.debug("ROP service is starting");
+
+ this.contextFactory = contextFactory;
+ initEventBridgeParameters(eventBridgeProperties);
+
+ logger.debug(getClass().getName() + " started");
+ }
+
+ public String getEventBridgeFactoryName() {
+ return eventBridgeFactoryName;
+ }
+
+ public Map<String, String> getEventBridgeParameters() {
+ return eventBridgeParameters != null ? Collections.unmodifiableMap(eventBridgeParameters)
+ : Collections.EMPTY_MAP;
+ }
+
+ /**
+ * Creates a new ServerSession with a dedicated DataChannel.
+ */
+ protected abstract ServerSession createServerSession();
+
+ /**
+ * Creates a new ServerSession based on a shared DataChannel.
+ *
+ * @param name
+ * shared session name used to lookup a shared DataChannel.
+ */
+ protected abstract ServerSession createServerSession(String name);
+
+ /**
+ * Returns a ServerSession object that represents Cayenne-related state
+ * associated with the current session. If ServerSession hasn't been
+ * previously saved, returns null.
+ */
+ protected abstract ServerSession getServerSession();
+
+ @Override
+ public RemoteSession establishSession() {
+ logger.debug("Session requested by client");
+
+ RemoteSession session = createServerSession().getSession();
+
+ logger.debug("Established client session: " + session);
+ return session;
+ }
+
+ @Override
+ public RemoteSession establishSharedSession(String name) {
+ logger.debug("Shared session requested by client. Group name: " + name);
+
+ if (name == null) {
+ throw new CayenneRuntimeException("Invalid null shared session name");
+ }
+
+ return createServerSession(name).getSession();
+ }
+
+ @Override
+ public Object processMessage(ClientMessage message) throws Throwable {
+
+ if (message == null) {
+ throw new IllegalArgumentException("Null client message.");
+ }
+
+ ServerSession handler = getServerSession();
+
+ if (handler == null) {
+ throw new MissingSessionException("No session associated with request.");
+ }
+
+ logger.debug("processMessage, sessionId: " + handler.getSession().getSessionId());
+
+ // intercept and log exceptions
+ try {
+ return DispatchHelper.dispatch(handler.getChannel(), message);
+ } catch (Throwable th) {
+
+ StringBuilder wrapperMessage = new StringBuilder();
+ wrapperMessage.append("Exception processing message ").append(message.getClass().getName())
+ .append(" of type ").append(message);
+
+ String wrapperMessageString = wrapperMessage.toString();
+ logger.info(wrapperMessageString, th);
+
+ // This exception will probably be propagated to the client.
+ // Recast the exception to a serializable form.
+ Exception cause = new Exception(Util.unwindException(th).getLocalizedMessage());
+
+ throw new CayenneRuntimeException(wrapperMessageString, cause);
+ }
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ }
+
+ protected RemoteSession createRemoteSession(String sessionId, String name, boolean enableEvents) {
+ RemoteSession session = (enableEvents) ? new RemoteSession(sessionId, eventBridgeFactoryName,
+ eventBridgeParameters) : new RemoteSession(sessionId);
+
+ session.setName(name);
+ return session;
+ }
+
+ /**
+ * Creates a server-side channel that will handle all client requests. For
+ * shared sessions the same channel instance is reused for the entire group
+ * of clients. For dedicated sessions, one channel per client is created.
+ * <p>
+ * This implementation returns {@link ClientServerChannel} instance wrapping
+ * a DataContext. Subclasses may override the method to customize channel
+ * creation. For instance they may wrap channel in the custom interceptors
+ * to handle transactions or security.
+ */
+ protected DataChannel createChannel() {
+ return new ClientServerChannel((DataContext) contextFactory.createContext());
+ }
+
+ /**
+ * Initializes EventBridge parameters for remote clients peer-to-peer
+ * communications.
+ */
+ protected void initEventBridgeParameters(Map<String, String> properties) {
+ String eventBridgeFactoryName = properties.get(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY);
+
+ if (eventBridgeFactoryName != null) {
+
+ Map<String, String> eventBridgeParameters = new HashMap<>(properties);
+ eventBridgeParameters.remove(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY);
+
+ this.eventBridgeFactoryName = eventBridgeFactoryName;
+ this.eventBridgeParameters = eventBridgeParameters;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
new file mode 100644
index 0000000..83b23e2
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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.remote.service;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.remote.BootstrapMessage;
+import org.apache.cayenne.remote.ClientMessage;
+import org.apache.cayenne.remote.QueryMessage;
+import org.apache.cayenne.remote.SyncMessage;
+
+/**
+ * A helper class to match message types with DataChannel methods.
+ *
+ * @since 1.2
+ */
+class DispatchHelper {
+
+ static Object dispatch(DataChannel channel, ClientMessage message) {
+ // do most common messages first...
+ if (message instanceof QueryMessage) {
+ return channel.onQuery(null, ((QueryMessage) message).getQuery());
+ } else if (message instanceof SyncMessage) {
+ SyncMessage sync = (SyncMessage) message;
+ return channel.onSync(null, sync.getSenderChanges(), sync.getType());
+ } else if (message instanceof BootstrapMessage) {
+ return channel.getEntityResolver().getClientEntityResolver();
+ } else {
+ throw new CayenneRuntimeException("Message dispatch error. Unsupported message: %s", message);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
new file mode 100644
index 0000000..80bd9bd
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
@@ -0,0 +1,134 @@
+/*****************************************************************
+ * 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.remote.service;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.remote.RemoteSession;
+
+/**
+ * A {@link org.apache.cayenne.remote.RemoteService} implementation that stores server
+ * context information in HTTP sessions.
+ *
+ * @since 1.2
+ */
+public abstract class HttpRemoteService extends BaseRemoteService {
+
+ static final String SESSION_ATTRIBUTE = HttpRemoteService.class.getName()
+ + ".ServerSession";
+
+ private Map<String, WeakReference<DataChannel>> sharedChannels;
+
+ /**
+ * @since 3.1
+ */
+ public HttpRemoteService(ObjectContextFactory contextFactory,
+ Map<String, String> eventBridgeProperties) {
+ super(contextFactory, eventBridgeProperties);
+ this.sharedChannels = new HashMap<>();
+ }
+
+ /**
+ * Returns an HttpSession associated with the current request in progress.
+ */
+ protected abstract HttpSession getSession(boolean create);
+
+ /**
+ * Returns a ServerSession object that represents Cayenne-related state associated
+ * with the current session. If ServerSession hasn't been previously saved, returns
+ * null.
+ */
+ @Override
+ protected ServerSession getServerSession() {
+ HttpSession httpSession = getSession(true);
+ return (ServerSession) httpSession.getAttribute(SESSION_ATTRIBUTE);
+ }
+
+ /**
+ * Creates a new ServerSession with a dedicated DataChannel. Returned ServerSession is
+ * stored in HttpSession for future reuse.
+ */
+ @Override
+ protected ServerSession createServerSession() {
+
+ HttpSession httpSession = getSession(true);
+
+ DataChannel channel = createChannel();
+ RemoteSession remoteSession = createRemoteSession(
+ httpSession.getId(),
+ null,
+ false);
+ ServerSession serverSession = new ServerSession(remoteSession, channel);
+
+ httpSession.setAttribute(SESSION_ATTRIBUTE, serverSession);
+ return serverSession;
+ }
+
+ /**
+ * Creates a new ServerSession based on a shared DataChannel. Returned ServerSession
+ * is stored in HttpSession for future reuse.
+ *
+ * @param name shared session name used to lookup a shared DataChannel.
+ */
+ @Override
+ protected ServerSession createServerSession(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name is null for shared session.");
+ }
+
+ HttpSession httpSession = getSession(true);
+ DataChannel channel;
+
+ synchronized (sharedChannels) {
+ channel = getSharedChannel(name);
+ if (channel == null) {
+ channel = createChannel();
+ saveSharedChannel(name, channel);
+ logger.debug("Starting a new shared channel: " + name);
+ }
+ else {
+ logger.debug("Joining existing shared channel: " + name);
+ }
+ }
+
+ RemoteSession remoteSession = createRemoteSession(httpSession.getId(), name, true);
+
+ ServerSession serverSession = new ServerSession(remoteSession, channel);
+ httpSession.setAttribute(SESSION_ATTRIBUTE, serverSession);
+ return serverSession;
+ }
+
+ protected DataChannel getSharedChannel(String name) {
+ WeakReference<DataChannel> ref = sharedChannels.get(name);
+ return (ref != null) ? ref.get() : null;
+ }
+
+ protected void saveSharedChannel(String name, DataChannel channel) {
+ // wrap value in a WeakReference so that channels can be deallocated when all
+ // sessions that reference this channel time out...
+ sharedChannels.put(name, new WeakReference<DataChannel>(channel));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java
new file mode 100644
index 0000000..e46e50b
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.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.remote.service;
+
+import org.apache.cayenne.CayenneRuntimeException;
+
+/**
+ * An exception that are thrown by the ROP server if the client are missing a session.
+ *
+ * @since 3.0
+ */
+public class MissingSessionException extends CayenneRuntimeException {
+
+ public MissingSessionException() {
+ super();
+ }
+
+ public MissingSessionException(String messageFormat, Object... messageArgs) {
+ super(messageFormat, messageArgs);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
new file mode 100644
index 0000000..2ae8904
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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.remote.service;
+
+import java.io.Serializable;
+
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.remote.RemoteSession;
+
+/**
+ * An object that stores server side objects for the client session.
+ *
+ * @since 1.2
+ */
+public class ServerSession implements Serializable {
+
+ protected RemoteSession session;
+ protected DataChannel channel;
+
+ public ServerSession(RemoteSession session, DataChannel channel) {
+ this.session = session;
+ this.channel = channel;
+ }
+
+ public DataChannel getChannel() {
+ return channel;
+ }
+
+ public RemoteSession getSession() {
+ return session;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
new file mode 100644
index 0000000..1adfb52
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
@@ -0,0 +1,67 @@
+/*****************************************************************
+ * 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;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * @since 4.0
+ */
+public class CayenneContextGraphManagerTest {
+
+ private CayenneContextGraphManager graphManager;
+
+ @Before
+ public void before() {
+ CayenneContext mockContext = mock(CayenneContext.class);
+ this.graphManager = new CayenneContextGraphManager(mockContext, false, false);
+ }
+
+ @Test
+ public void testRegisterNode() {
+
+ ObjectId id = new ObjectId("E1", "ID", 500);
+ Persistent object = mock(Persistent.class);
+
+ graphManager.registerNode(id, object);
+ assertSame(object, graphManager.getNode(id));
+ }
+
+ @Test
+ public void testUnregisterNode() {
+
+ ObjectId id = new ObjectId("E1", "ID", 500);
+ Persistent object = mock(Persistent.class);
+
+ graphManager.registerNode(id, object);
+ Object unregistered = graphManager.unregisterNode(id);
+ assertSame(object, unregistered);
+
+ verify(object, times(0)).setObjectId(null);
+ verify(object).setObjectContext(null);
+ verify(object).setPersistenceState(PersistenceState.TRANSIENT);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
new file mode 100644
index 0000000..f1ecab2
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
@@ -0,0 +1,40 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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.remote;
+
+import java.rmi.RemoteException;
+
+public class MockRemoteService implements RemoteService {
+
+ public RemoteSession establishSession() throws RemoteException {
+ return null;
+ }
+
+ public RemoteSession establishSharedSession(String name) throws RemoteException {
+ return null;
+ }
+
+ public Object processMessage(ClientMessage message) throws RemoteException, Throwable {
+ return null;
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java
new file mode 100644
index 0000000..869e110
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.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.remote;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class RemoteSessionTest {
+
+ @Test
+ public void testConstructor1() {
+ RemoteSession descriptor = new RemoteSession("abc");
+ assertEquals("abc", descriptor.getSessionId());
+ assertFalse(descriptor.isServerEventsEnabled());
+ }
+
+ @Test
+ public void testConstructor2() {
+ RemoteSession descriptor = new RemoteSession("abc", "factory", null);
+ assertEquals("abc", descriptor.getSessionId());
+ assertTrue(descriptor.isServerEventsEnabled());
+ }
+
+ @Test
+ public void testHashCode() {
+ RemoteSession d1 = new RemoteSession("1");
+ RemoteSession d2 = new RemoteSession("1");
+
+ assertEquals(d1.hashCode(), d1.hashCode());
+ assertEquals(d1.hashCode(), d2.hashCode());
+
+ d2.setName("some name");
+ assertEquals(d1.hashCode(), d2.hashCode());
+
+ RemoteSession d3 = new RemoteSession("2");
+ assertFalse(d1.hashCode() == d3.hashCode());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java
new file mode 100644
index 0000000..4cf0d0b
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.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.remote.hessian;
+
+import com.caucho.hessian.io.AbstractSerializerFactory;
+import com.caucho.hessian.io.SerializerFactory;
+import org.apache.cayenne.map.EntityResolver;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class HessianConfigTest {
+
+ @Test
+ public void testLoadFactoryNoExtensions() {
+ SerializerFactory factory = HessianConfig.createFactory(null, null);
+ assertNotNull(factory);
+ }
+
+ @Test
+ public void testLoadFactoryNoInjection() throws Exception {
+ AbstractSerializerFactory factory = HessianConfig.loadFactory(
+ MockAbstractSerializerFactory.class.getName(),
+ null);
+
+ assertTrue(factory instanceof MockAbstractSerializerFactory);
+ assertNull(((MockAbstractSerializerFactory) factory).getEntityResolver());
+ }
+
+ @Test
+ public void testLoadFactoryInjection() throws Exception {
+ EntityResolver resolver = new EntityResolver();
+ AbstractSerializerFactory factory = HessianConfig.loadFactory(
+ MockAbstractSerializerFactory.class.getName(),
+ resolver);
+
+ assertTrue(factory instanceof MockAbstractSerializerFactory);
+ assertSame(resolver, ((MockAbstractSerializerFactory) factory)
+ .getEntityResolver());
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
new file mode 100644
index 0000000..8e861ab
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
@@ -0,0 +1,51 @@
+/*****************************************************************
+ * 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.remote.hessian;
+
+import org.apache.cayenne.map.EntityResolver;
+
+import com.caucho.hessian.io.AbstractSerializerFactory;
+import com.caucho.hessian.io.Deserializer;
+import com.caucho.hessian.io.HessianProtocolException;
+import com.caucho.hessian.io.Serializer;
+
+public class MockAbstractSerializerFactory extends AbstractSerializerFactory {
+
+ protected EntityResolver entityResolver;
+
+ @Override
+ public Serializer getSerializer(Class cl) throws HessianProtocolException {
+ return null;
+ }
+
+ @Override
+ public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
+ return null;
+ }
+
+ public EntityResolver getEntityResolver() {
+ return entityResolver;
+ }
+
+ public void setEntityResolver(EntityResolver entityResolver) {
+ this.entityResolver = entityResolver;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
new file mode 100644
index 0000000..590651d
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
@@ -0,0 +1,73 @@
+/*****************************************************************
+ * 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.remote.hessian.service;
+
+import com.caucho.services.server.ServiceContext;
+import com.mockrunner.mock.web.MockHttpServletRequest;
+import com.mockrunner.mock.web.MockHttpSession;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.event.MockEventBridgeFactory;
+import org.junit.Test;
+
+import javax.servlet.http.HttpSession;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertSame;
+
+public class HessianServiceTest {
+
+ @Test
+ public void testGetSession() throws Exception {
+
+ Map<String, String> map = new HashMap<>();
+ map.put(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY, MockEventBridgeFactory.class.getName());
+
+ ObjectContextFactory factory = new ObjectContextFactory() {
+
+ public ObjectContext createContext(DataChannel parent) {
+ return null;
+ }
+
+ public ObjectContext createContext() {
+ return null;
+ }
+ };
+ HessianService service = new HessianService(factory, map);
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ HttpSession session = new MockHttpSession();
+ request.setSession(session);
+
+ // for some reason need to call this to get session activated in the
+ // mock request
+ request.getSession();
+
+ try {
+ ServiceContext.begin(request, null, null, null);
+ assertSame(session, service.getSession(false));
+ } finally {
+ ServiceContext.end();
+ }
+ }
+}
[2/5] cayenne git commit: CAY-2373 cayenne-rop-server module - move
org.apache.cayenne.remote package to cayenne-rop server module - remove
dependencies from cayenne-server pom.xml - update tutorial
Posted by nt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
deleted file mode 100644
index 7ed1004..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteIncrementalFaultList.java
+++ /dev/null
@@ -1,668 +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.remote;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.util.IDUtil;
-import org.apache.cayenne.util.IncrementalListResponse;
-import org.apache.cayenne.util.Util;
-
-/**
- * A list that serves as a container of Persistent objects. It is usually returned by an
- * ObjectContext when a paginated query is performed. Initially only the first "page" of
- * objects is fully resolved. Pages following the first page are resolved on demand. When
- * a list element is accessed, the list would ensure that this element as well as all its
- * siblings on the same page are fully resolved.
- * <p>
- * The list can hold DataRows or Persistent objects. Attempts to add any other object
- * types will result in an exception.
- * </p>
- * <p>
- * Certain operations like <code>toArray</code> would trigger full list fetch.
- * </p>
- * <p>
- * Synchronization Note: this list is not synchronized. All access to it should follow
- * synchronization rules applicable for ArrayList.
- * </p>
- *
- * @since 1.2
- */
-public class RemoteIncrementalFaultList implements List {
-
- static final Object PLACEHOLDER = new Object();
-
- protected List elements;
-
- protected String cacheKey;
- protected int pageSize;
- protected int unfetchedObjects;
- protected Query paginatedQuery;
-
- protected transient ObjectContext context;
-
- /**
- * Stores a hint allowing to distinguish data rows from unfetched ids when the query
- * fetches data rows.
- */
- protected int rowWidth;
-
- private ListHelper helper;
-
- public RemoteIncrementalFaultList(ObjectContext context, Query paginatedQuery) {
-
- QueryMetadata metadata = paginatedQuery.getMetaData(context.getEntityResolver());
-
- if (metadata.getPageSize() <= 0) {
- throw new IllegalArgumentException("Page size must be positive: "
- + metadata.getPageSize());
- }
-
- this.pageSize = metadata.getPageSize();
- this.helper = (metadata.isFetchingDataRows())
- ? new DataRowListHelper()
- : new PersistentListHelper();
- this.context = context;
-
- // use provided cache key if possible; this would allow clients to
- // address the same server-side list from multiple queries.
- this.cacheKey = metadata.getCacheKey();
- if (cacheKey == null) {
- cacheKey = generateCacheKey();
- }
-
- Query query = paginatedQuery;
-
- // always wrap a query in a Incremental*Query, to ensure cache key is
- // client-generated (e.g. see CAY-1003 - client and server can be in different
- // timezones, so the key can be messed up)
-
- // there are some serious pagination optimizations for SelectQuery on the
- // server-side, so use a special wrapper that is itself a subclass of
- // SelectQuery
- if (query instanceof SelectQuery) {
- query = new IncrementalSelectQuery<Object>((SelectQuery<Object>) paginatedQuery, cacheKey);
- }
- else {
- query = new IncrementalQuery(paginatedQuery, cacheKey);
- }
-
- // ensure that originating query is wrapped to include the right cache key....
- this.paginatedQuery = query;
-
- // select directly from the channel, bypassing the context. Otherwise our query
- // wrapper can be intercepted incorrectly
- QueryResponse response = context.getChannel().onQuery(context, query);
-
- List firstPage = response.firstList();
-
- // sanity check
- if (firstPage.size() > pageSize) {
- throw new IllegalArgumentException("Returned page size ("
- + firstPage.size()
- + ") exceeds requested page size ("
- + pageSize
- + ")");
- }
- // result is smaller than a page
- else if (firstPage.size() < pageSize) {
- this.elements = new ArrayList(firstPage);
- unfetchedObjects = 0;
- }
- else {
-
- if (response instanceof IncrementalListResponse) {
- int fullListSize = ((IncrementalListResponse) response).getFullSize();
-
- this.unfetchedObjects = fullListSize - firstPage.size();
- this.elements = new ArrayList(fullListSize);
- elements.addAll(firstPage);
-
- // fill the rest with placeholder...
- for (int i = pageSize; i < fullListSize; i++) {
- elements.add(PLACEHOLDER);
- }
- }
- // this happens when full size equals page size
- else {
- this.elements = new ArrayList(firstPage);
- unfetchedObjects = 0;
- }
- }
- }
-
- private String generateCacheKey() {
- byte[] bytes = IDUtil.pseudoUniqueByteSequence8();
- StringBuffer buffer = new StringBuffer(17);
- buffer.append("I");
- for (byte aByte : bytes) {
- IDUtil.appendFormattedByte(buffer, aByte);
- }
-
- return buffer.toString();
- }
-
- /**
- * Will resolve all unread objects.
- */
- public void resolveAll() {
- resolveInterval(0, size());
- }
-
- /**
- * @param object
- * @return <code>true</code> if the object corresponds to an unresolved state and
- * does require a fetch before being returned to the user.
- */
- private boolean isUnresolved(Object object) {
- return object == PLACEHOLDER;
- }
-
- /**
- * Resolves a sublist of objects starting at <code>fromIndex</code> up to but not
- * including <code>toIndex</code>. Internally performs bound checking and trims
- * indexes accordingly.
- */
- protected void resolveInterval(int fromIndex, int toIndex) {
- if (fromIndex >= toIndex || elements.isEmpty()) {
- return;
- }
-
- if (context == null) {
- throw new CayenneRuntimeException("No ObjectContext set, can't resolve objects.");
- }
-
- // bounds checking
-
- if (fromIndex < 0) {
- fromIndex = 0;
- }
-
- if (toIndex > elements.size()) {
- toIndex = elements.size();
- }
-
- // find disjoint ranges and resolve them individually...
-
- int fromPage = pageIndex(fromIndex);
- int toPage = pageIndex(toIndex - 1);
-
- int rangeStartIndex = -1;
- for (int i = fromPage; i <= toPage; i++) {
-
- int pageStartIndex = i * pageSize;
- Object firstPageObject = elements.get(pageStartIndex);
- if (isUnresolved(firstPageObject)) {
-
- // start range
- if (rangeStartIndex < 0) {
- rangeStartIndex = pageStartIndex;
- }
- }
- else {
-
- // finish range...
- if (rangeStartIndex >= 0) {
- forceResolveInterval(rangeStartIndex, pageStartIndex);
- rangeStartIndex = -1;
- }
- }
- }
-
- // load last page
- if (rangeStartIndex >= 0) {
- forceResolveInterval(rangeStartIndex, toIndex);
- }
- }
-
- void forceResolveInterval(int fromIndex, int toIndex) {
-
- int pastEnd = toIndex - size();
- if (pastEnd > 0) {
- toIndex = size();
- }
-
- int fetchLimit = toIndex - fromIndex;
-
- RangeQuery query = new RangeQuery(cacheKey, fromIndex, fetchLimit, paginatedQuery);
-
- List sublist = context.performQuery(query);
-
- // sanity check
- if (sublist.size() != fetchLimit) {
- throw new CayenneRuntimeException("Resolved range size %d is not the same as expected: %d"
- , sublist.size(), fetchLimit);
- }
-
- for (int i = 0; i < fetchLimit; i++) {
- elements.set(fromIndex + i, sublist.get(i));
- }
-
- unfetchedObjects -= sublist.size();
- }
-
- /**
- * Returns zero-based index of the virtual "page" for a given array element index.
- */
- int pageIndex(int elementIndex) {
- if (elementIndex < 0 || elementIndex > size()) {
- throw new IndexOutOfBoundsException("Index: " + elementIndex);
- }
-
- if (pageSize <= 0 || elementIndex < 0) {
- return -1;
- }
-
- return elementIndex / pageSize;
- }
-
- /**
- * Returns ObjectContext associated with this list.
- */
- public ObjectContext getContext() {
- return context;
- }
-
- /**
- * Returns the pageSize.
- *
- * @return int
- */
- public int getPageSize() {
- return pageSize;
- }
-
- /**
- * Returns a list iterator for this list. DataObjects are resolved a page (according
- * to getPageSize()) at a time as necessary - when retrieved with next() or
- * previous().
- */
- public ListIterator listIterator() {
- return new ListIteratorHelper(0);
- }
-
- /**
- * Returns a list iterator of the elements in this list (in proper sequence), starting
- * at the specified position in this list. The specified index indicates the first
- * element that would be returned by an initial call to the next method. An initial
- * call to the previous method would return the element with the specified index minus
- * one. DataObjects are resolved a page at a time (according to getPageSize()) as
- * necessary - when retrieved with next() or previous().
- */
- public ListIterator listIterator(int index) {
- if (index < 0 || index > size()) {
- throw new IndexOutOfBoundsException("Index: " + index);
- }
-
- return new ListIteratorHelper(index);
- }
-
- /**
- * Return an iterator for this list. DataObjects are resolved a page (according to
- * getPageSize()) at a time as necessary - when retrieved with next().
- */
- public Iterator iterator() {
- // by virtue of get(index)'s implementation, resolution of ids into
- // objects will occur on pageSize boundaries as necessary.
- return new Iterator() {
-
- int listIndex = 0;
-
- public boolean hasNext() {
- return (listIndex < elements.size());
- }
-
- public Object next() {
- if (listIndex >= elements.size())
- throw new NoSuchElementException("no more elements");
-
- return get(listIndex++);
- }
-
- public void remove() {
- throw new UnsupportedOperationException("remove not supported.");
- }
- };
- }
-
- /**
- * @see java.util.List#add(int, Object)
- */
- public void add(int index, Object element) {
- helper.validateListObject(element);
- elements.add(index, element);
-
- }
-
- /**
- * @see java.util.Collection#add(Object)
- */
- public boolean add(Object o) {
- helper.validateListObject(o);
- return elements.add(o);
- }
-
- /**
- * @see java.util.Collection#addAll(Collection)
- */
- public boolean addAll(Collection c) {
-
- return elements.addAll(c);
-
- }
-
- /**
- * @see java.util.List#addAll(int, Collection)
- */
- public boolean addAll(int index, Collection c) {
-
- return elements.addAll(index, c);
-
- }
-
- /**
- * @see java.util.Collection#clear()
- */
- public void clear() {
- elements.clear();
- }
-
- /**
- * @see java.util.Collection#contains(Object)
- */
- public boolean contains(Object o) {
- return indexOf(o) >= 0;
- }
-
- /**
- * @see java.util.Collection#containsAll(Collection)
- */
- public boolean containsAll(Collection c) {
- Iterator it = c.iterator();
- while (it.hasNext()) {
- if (!contains(it.next())) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @see java.util.List#get(int)
- */
- public Object get(int index) {
-
- Object o = elements.get(index);
-
- if (isUnresolved(o)) {
- // read this page
- int pageStart = pageIndex(index) * pageSize;
- resolveInterval(pageStart, pageStart + pageSize);
-
- return elements.get(index);
- }
- else {
- return o;
- }
-
- }
-
- /**
- * @see java.util.List#indexOf(Object)
- */
- public int indexOf(Object o) {
- return helper.indexOfObject(o);
- }
-
- /**
- * @see java.util.Collection#isEmpty()
- */
- public boolean isEmpty() {
-
- return elements.isEmpty();
-
- }
-
- /**
- * @see java.util.List#lastIndexOf(Object)
- */
- public int lastIndexOf(Object o) {
- return helper.lastIndexOfObject(o);
- }
-
- /**
- * @see java.util.List#remove(int)
- */
- public Object remove(int index) {
-
- return elements.remove(index);
-
- }
-
- /**
- * @see java.util.Collection#remove(Object)
- */
- public boolean remove(Object o) {
-
- return elements.remove(o);
-
- }
-
- /**
- * @see java.util.Collection#removeAll(Collection)
- */
- public boolean removeAll(Collection c) {
-
- return elements.removeAll(c);
-
- }
-
- /**
- * @see java.util.Collection#retainAll(Collection)
- */
- public boolean retainAll(Collection c) {
-
- return elements.retainAll(c);
-
- }
-
- /**
- * @see java.util.List#set(int, Object)
- */
- public Object set(int index, Object element) {
- helper.validateListObject(element);
-
- return elements.set(index, element);
-
- }
-
- /**
- * @see java.util.Collection#size()
- */
- public int size() {
- return elements.size();
- }
-
- public List subList(int fromIndex, int toIndex) {
- resolveInterval(fromIndex, toIndex);
- return elements.subList(fromIndex, toIndex);
- }
-
- public Object[] toArray() {
- resolveAll();
-
- return elements.toArray();
- }
-
- /**
- * @see java.util.Collection#toArray(Object[])
- */
- public Object[] toArray(Object[] a) {
- resolveAll();
-
- return elements.toArray(a);
- }
-
- /**
- * Returns a total number of objects that are not resolved yet.
- */
- public int getUnfetchedObjects() {
- return unfetchedObjects;
- }
-
- abstract class ListHelper {
-
- int indexOfObject(Object object) {
- if (incorrectObjectType(object)) {
- return -1;
- }
-
- for (int i = 0; i < elements.size(); i++) {
-
- if (Util.nullSafeEquals(object, get(i))) {
- return i;
- }
- }
-
- return -1;
- }
-
- int lastIndexOfObject(Object object) {
- if (incorrectObjectType(object)) {
- return -1;
- }
-
- for (int i = elements.size() - 1; i >= 0; i--) {
- if (Util.nullSafeEquals(object, get(i))) {
- return i;
- }
- }
-
- return -1;
- }
-
- abstract boolean incorrectObjectType(Object object);
-
- void validateListObject(Object object) throws IllegalArgumentException {
- if (incorrectObjectType(object)) {
- throw new IllegalArgumentException("Can't store this object: " + object);
- }
- }
- }
-
- class PersistentListHelper extends ListHelper {
-
- @Override
- boolean incorrectObjectType(Object object) {
- if (!(object instanceof Persistent)) {
- return true;
- }
-
- Persistent persistent = (Persistent) object;
- if (persistent.getObjectContext() != context) {
- return true;
- }
-
- return false;
- }
-
- }
-
- class DataRowListHelper extends ListHelper {
-
- @Override
- boolean incorrectObjectType(Object object) {
- if (!(object instanceof Map)) {
- return true;
- }
-
- Map map = (Map) object;
- return map.size() != rowWidth;
- }
- }
-
- class ListIteratorHelper implements ListIterator {
-
- // by virtue of get(index)'s implementation, resolution of ids into
- // objects will occur on pageSize boundaries as necessary.
-
- int listIndex;
-
- public ListIteratorHelper(int startIndex) {
- this.listIndex = startIndex;
- }
-
- public void add(Object o) {
- throw new UnsupportedOperationException("add operation not supported");
- }
-
- public boolean hasNext() {
- return (listIndex < elements.size());
- }
-
- public boolean hasPrevious() {
- return (listIndex > 0);
- }
-
- public Object next() {
- if (listIndex >= elements.size())
- throw new NoSuchElementException("at the end of the list");
-
- return get(listIndex++);
- }
-
- public int nextIndex() {
- return listIndex;
- }
-
- public Object previous() {
- if (listIndex < 1)
- throw new NoSuchElementException("at the beginning of the list");
-
- return get(--listIndex);
- }
-
- public int previousIndex() {
- return (listIndex - 1);
- }
-
- public void remove() {
- throw new UnsupportedOperationException("remove operation not supported");
- }
-
- public void set(Object o) {
- throw new UnsupportedOperationException("set operation not supported");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
deleted file mode 100644
index f357846..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
+++ /dev/null
@@ -1,54 +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
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * 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.remote;
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-
-/**
- * Interface of a Cayenne remote service.
- *
- * @since 1.2
- * @see org.apache.cayenne.rop.ROPServlet
- */
-public interface RemoteService extends Remote {
-
- /**
- * Establishes a dedicated session with Cayenne DataChannel, returning session id.
- */
- RemoteSession establishSession() throws RemoteException;
-
- /**
- * Creates a new session with the specified or joins an existing one. This method is
- * used to bootstrap collaborating clients of a single "group chat".
- */
- RemoteSession establishSharedSession(String name) throws RemoteException;
-
- /**
- * Processes message on a remote server, returning the result of such processing.
- */
- Object processMessage(ClientMessage message) throws RemoteException, Throwable;
-
- /**
- * Close remote service resources.
- * @sine 4.0
- */
- void close() throws RemoteException;
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
deleted file mode 100644
index 461e79d..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteSession.java
+++ /dev/null
@@ -1,145 +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.remote;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.event.EventBridge;
-import org.apache.cayenne.event.EventBridgeFactory;
-import org.apache.cayenne.event.EventSubject;
-import org.apache.cayenne.util.HashCodeBuilder;
-import org.apache.cayenne.util.ToStringBuilder;
-
-/**
- * A descriptor used by default service implementation to pass session parameters to the
- * client. It provides the client with details on how to invoke the service and how to
- * listen for the server events.
- *
- * @since 1.2
- */
-public class RemoteSession implements Serializable {
-
- static final Collection<EventSubject> SUBJECTS = Arrays.asList(
- DataChannel.GRAPH_CHANGED_SUBJECT,
- DataChannel.GRAPH_FLUSHED_SUBJECT,
- DataChannel.GRAPH_ROLLEDBACK_SUBJECT);
-
- protected String name;
- protected String sessionId;
-
- protected String eventBridgeFactory;
- protected Map<String, String> eventBridgeParameters;
-
- // private constructor used by hessian deserialization mechanism
- @SuppressWarnings("unused")
- private RemoteSession() {
-
- }
-
- /**
- * Creates a HessianServiceDescriptor without server events support.
- */
- public RemoteSession(String sessionId) {
- this(sessionId, null, null);
- }
-
- /**
- * Creates a HessianServiceDescriptor. If <code>eventBridgeFactory</code> argument
- * is not null, session will support server events.
- */
- public RemoteSession(String sessionId, String eventBridgeFactory,
- Map<String, String> eventBridgeParameters) {
-
- if (sessionId == null) {
- throw new IllegalArgumentException("Null sessionId");
- }
-
- this.sessionId = sessionId;
- this.eventBridgeFactory = eventBridgeFactory;
- this.eventBridgeParameters = eventBridgeParameters;
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder(71, 5).append(sessionId).toHashCode();
- }
-
- /**
- * Returns server session id. This is often the same as HttpSession id.
- */
- public String getSessionId() {
- return sessionId;
- }
-
- /**
- * Returns session group name. Group name is used for shared sessions.
- */
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public boolean isServerEventsEnabled() {
- return eventBridgeFactory != null;
- }
-
- @Override
- public String toString() {
- ToStringBuilder builder = new ToStringBuilder(this)
- .append("sessionId", sessionId);
-
- if (eventBridgeFactory != null) {
- builder.append("eventBridgeFactory", eventBridgeFactory);
- }
-
- if (name != null) {
- builder.append("name", name);
- }
-
- return builder.toString();
- }
-
- public static Collection<EventSubject> getSubjects() {
- return SUBJECTS;
- }
-
- /**
- * @since 4.0
- */
- public String getEventBridgeFactory() {
- return eventBridgeFactory;
- }
-
- /**
- * @since 4.0
- */
- public Map<String, String> getEventBridgeParameters() {
- return eventBridgeParameters != null ? eventBridgeParameters : Collections.<String, String> emptyMap();
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
deleted file mode 100644
index d8d5f37..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/SyncMessage.java
+++ /dev/null
@@ -1,91 +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.remote;
-
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.graph.GraphDiff;
-
-/**
- * A message used for synchronization of the child with parent. It defines a few types of
- * synchronization: "flush" (when the child sends its changes without a commit), "commit"
- * (cascading flush with ultimate commit to the database), and "rollback" - cascading
- * reverting of all uncommitted changes.
- *
- * @since 1.2
- */
-public class SyncMessage implements ClientMessage {
-
- protected transient ObjectContext source;
- protected int type;
- protected GraphDiff senderChanges;
-
- // private constructor for Hessian deserialization
- @SuppressWarnings("unused")
- private SyncMessage() {
-
- }
-
- public SyncMessage(ObjectContext source, int syncType, GraphDiff senderChanges) {
- // validate type
- if (syncType != DataChannel.FLUSH_NOCASCADE_SYNC
- && syncType != DataChannel.FLUSH_CASCADE_SYNC
- && syncType != DataChannel.ROLLBACK_CASCADE_SYNC) {
- throw new IllegalArgumentException("'type' is invalid: " + syncType);
- }
-
- this.source = source;
- this.type = syncType;
- this.senderChanges = senderChanges;
- }
-
- /**
- * Returns a source of SyncMessage.
- */
- public ObjectContext getSource() {
- return source;
- }
-
- public int getType() {
- return type;
- }
-
- public GraphDiff getSenderChanges() {
- return senderChanges;
- }
-
- /**
- * Returns a description of the type of message.
- * Possibilities are "flush-sync", "flush-cascade-sync", "rollback-cascade-sync" or "unknown-sync".
- */
- @Override
- public String toString() {
- switch (type) {
- case DataChannel.FLUSH_NOCASCADE_SYNC:
- return "flush-sync";
- case DataChannel.FLUSH_CASCADE_SYNC:
- return "flush-cascade-sync";
- case DataChannel.ROLLBACK_CASCADE_SYNC:
- return "rollback-cascade-sync";
- default:
- return "unknown-sync";
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
deleted file mode 100644
index 98a794c..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/CayenneSerializerFactory.java
+++ /dev/null
@@ -1,43 +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.remote.hessian;
-
-import com.caucho.hessian.io.AbstractSerializerFactory;
-import com.caucho.hessian.io.Deserializer;
-import com.caucho.hessian.io.HessianProtocolException;
-
-// This class is an ugly workaround for Hessian 4 bug with not loading custom deserializers.
-// TODO: once it is fixed in Hessian, remove this class
-class CayenneSerializerFactory extends com.caucho.hessian.io.SerializerFactory {
- @Override
- public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
- for (int i = 0; _factories != null && i < _factories.size(); i++) {
- AbstractSerializerFactory factory;
- factory = (AbstractSerializerFactory) _factories.get(i);
-
- Deserializer deserializer = factory.getDeserializer(cl);
- if (deserializer != null) {
- return deserializer;
- }
- }
-
- return super.getDeserializer(cl);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
deleted file mode 100644
index 550ef09..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/HessianConfig.java
+++ /dev/null
@@ -1,114 +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.remote.hessian;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.util.Util;
-
-import com.caucho.hessian.io.AbstractSerializerFactory;
-import com.caucho.hessian.io.SerializerFactory;
-
-/**
- * A utility class that configures Hessian serialization properties using reflection.
- *
- * @since 1.2
- */
-public class HessianConfig {
-
- /**
- * Creates a Hessian SerializerFactory configured with zero or more
- * AbstractSerializerFactory extensions. Extensions are specified as class names. This
- * method can inject EntityResolver if an extension factory class defines
- * <em>setEntityResolver(EntityResolver)</em> method.
- *
- * @param factoryNames an array of factory class names. Each class must be a concrete
- * subclass of <em>com.caucho.hessian.io.AbstractSerializerFactory</em>
- * and have a default constructor.
- * @param resolver if not null, EntityResolver will be injected into all factories
- * that implement <em>setEntityResolver(EntityResolver)</em> method.
- */
- public static SerializerFactory createFactory(
- String[] factoryNames,
- EntityResolver resolver) {
-
- SerializerFactory factory = new CayenneSerializerFactory();
-
- if (factoryNames != null && factoryNames.length > 0) {
-
- for (String factoryName : factoryNames) {
-
- try {
- factory.addFactory(loadFactory(factoryName, resolver));
- }
- catch (Exception e) {
- throw new CayenneRuntimeException("Error configuring factory class "
- + factoryName, e);
- }
- }
- }
-
- return factory;
- }
-
- static AbstractSerializerFactory loadFactory(
- String factoryName,
- EntityResolver resolver) throws Exception {
-
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- Class factoryClass = Class.forName(factoryName, true, loader);
-
- if (!AbstractSerializerFactory.class.isAssignableFrom(factoryClass)) {
- throw new IllegalArgumentException(factoryClass
- + " is not a AbstractSerializerFactory");
- }
-
- Constructor c = factoryClass.getDeclaredConstructor();
- if (!Util.isAccessible(c)) {
- c.setAccessible(true);
- }
-
- AbstractSerializerFactory object = (AbstractSerializerFactory) c.newInstance();
-
- if (resolver != null) {
- try {
-
- Method setter = factoryClass.getDeclaredMethod(
- "setEntityResolver",
- EntityResolver.class);
-
- if (!Util.isAccessible(setter)) {
- setter.setAccessible(true);
- }
-
- setter.invoke(object, resolver);
- }
- catch (Exception e) {
- // ignore injection exception
- }
- }
-
- return object;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
deleted file mode 100644
index 5409e43..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/HessianService.java
+++ /dev/null
@@ -1,64 +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.remote.hessian.service;
-
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-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 com.caucho.services.server.ServiceContext;
-
-/**
- * An implementation of RemoteService for work within Caucho Hessian environment.
- *
- * @since 3.1 the service API is reworked to initialize via Cayenne DI.
- */
-public class HessianService extends HttpRemoteService {
-
- public static final String[] SERVER_SERIALIZER_FACTORIES = new String[] {
- ServerSerializerFactory.class.getName()
- };
-
- /**
- * @since 3.1
- */
- public HessianService(@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);
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
deleted file mode 100644
index bcd9f52..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerDataRowSerializer.java
+++ /dev/null
@@ -1,56 +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.remote.hessian.service;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.cayenne.DataRow;
-
-import com.caucho.hessian.io.AbstractHessianOutput;
-import com.caucho.hessian.io.AbstractSerializer;
-
-/**
- * A server-side DataRow Hessian serializer.
- */
-class ServerDataRowSerializer extends AbstractSerializer {
-
- @Override
- public void writeObject(Object object, AbstractHessianOutput out) throws IOException {
- if (out.addRef(object)) {
- return;
- }
-
- DataRow row = (DataRow) object;
-
- out.writeMapBegin(DataRow.class.getName());
-
- out.writeInt(row.size());
- out.writeLong(row.getVersion());
- out.writeLong(row.getReplacesVersion());
-
- for (final Map.Entry<String, Object> entry : row.entrySet()) {
- out.writeObject(entry.getKey());
- out.writeObject(entry.getValue());
- }
-
- out.writeMapEnd();
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
deleted file mode 100644
index cf31ef0..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerPersistentObjectListSerializer.java
+++ /dev/null
@@ -1,50 +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.remote.hessian.service;
-
-import java.io.IOException;
-
-import org.apache.cayenne.util.PersistentObjectList;
-
-import com.caucho.hessian.io.AbstractHessianOutput;
-import com.caucho.hessian.io.JavaSerializer;
-
-/**
- * Serializer for PersistentObjectLists.
- *
- * @since 1.2
- */
-class ServerPersistentObjectListSerializer extends JavaSerializer {
-
- ServerPersistentObjectListSerializer() {
- super(PersistentObjectList.class);
- }
-
- @Override
- public void writeObject(Object object, AbstractHessianOutput out) throws IOException {
- PersistentObjectList list = (PersistentObjectList) object;
- if (list.isFault()) {
- out.writeNull();
- }
- else {
- super.writeObject(object, out);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/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
deleted file mode 100644
index 3310cee..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/hessian/service/ServerSerializerFactory.java
+++ /dev/null
@@ -1,71 +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.remote.hessian.service;
-
-import org.apache.cayenne.DataRow;
-import org.apache.cayenne.util.PersistentObjectList;
-import org.apache.cayenne.util.PersistentObjectMap;
-
-import com.caucho.hessian.io.AbstractSerializerFactory;
-import com.caucho.hessian.io.Deserializer;
-import com.caucho.hessian.io.HessianProtocolException;
-import com.caucho.hessian.io.JavaSerializer;
-import com.caucho.hessian.io.Serializer;
-
-/**
- * An object that manages all custom (de)serializers used on the server.
- *
- * @since 1.2
- */
-public class ServerSerializerFactory extends AbstractSerializerFactory {
- private ServerPersistentObjectListSerializer persistentObjectListSerializer;
- private ServerDataRowSerializer dataRowSerilaizer;
- private Serializer javaSerializer;
-
- ServerSerializerFactory() {
- this.persistentObjectListSerializer = new ServerPersistentObjectListSerializer();
- this.dataRowSerilaizer = new ServerDataRowSerializer();
- }
-
- @Override
- public Serializer getSerializer(Class cl) throws HessianProtocolException {
-
- if (PersistentObjectList.class.isAssignableFrom(cl)) {
- return persistentObjectListSerializer;
- }
- else if (DataRow.class.isAssignableFrom(cl)) {
- return dataRowSerilaizer;
- }
- //turns out Hessian uses its own (incorrect) serialization mechanism for maps
- else if (PersistentObjectMap.class.isAssignableFrom(cl)) {
- if (javaSerializer == null) {
- javaSerializer = new JavaSerializer(cl);
- }
- return javaSerializer;
- }
-
- return null;
- }
-
- @Override
- public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
deleted file mode 100644
index 68fd89d..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
+++ /dev/null
@@ -1,199 +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.remote.service;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.access.ClientServerChannel;
-import org.apache.cayenne.access.DataContext;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ObjectContextFactory;
-import org.apache.cayenne.remote.ClientMessage;
-import org.apache.cayenne.remote.RemoteService;
-import org.apache.cayenne.remote.RemoteSession;
-import org.apache.cayenne.util.Util;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.rmi.RemoteException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A generic implementation of an RemoteService. Can be subclassed to work with
- * different remoting mechanisms, such as Hessian or JAXRPC.
- *
- * @since 1.2
- */
-public abstract class BaseRemoteService implements RemoteService {
-
- // keep logger non-static so that it could be garbage collected with this
- // instance.
- protected final Logger logger;
-
- protected ObjectContextFactory contextFactory;
- protected String eventBridgeFactoryName;
- protected Map<String, String> eventBridgeParameters;
-
- /**
- * @since 3.1
- */
- public BaseRemoteService(ObjectContextFactory contextFactory, Map<String, String> eventBridgeProperties) {
-
- logger = LoggerFactory.getLogger(getClass());
-
- // start Cayenne service
- logger.debug("ROP service is starting");
-
- this.contextFactory = contextFactory;
- initEventBridgeParameters(eventBridgeProperties);
-
- logger.debug(getClass().getName() + " started");
- }
-
- public String getEventBridgeFactoryName() {
- return eventBridgeFactoryName;
- }
-
- public Map<String, String> getEventBridgeParameters() {
- return eventBridgeParameters != null ? Collections.unmodifiableMap(eventBridgeParameters)
- : Collections.EMPTY_MAP;
- }
-
- /**
- * Creates a new ServerSession with a dedicated DataChannel.
- */
- protected abstract ServerSession createServerSession();
-
- /**
- * Creates a new ServerSession based on a shared DataChannel.
- *
- * @param name
- * shared session name used to lookup a shared DataChannel.
- */
- protected abstract ServerSession createServerSession(String name);
-
- /**
- * Returns a ServerSession object that represents Cayenne-related state
- * associated with the current session. If ServerSession hasn't been
- * previously saved, returns null.
- */
- protected abstract ServerSession getServerSession();
-
- @Override
- public RemoteSession establishSession() {
- logger.debug("Session requested by client");
-
- RemoteSession session = createServerSession().getSession();
-
- logger.debug("Established client session: " + session);
- return session;
- }
-
- @Override
- public RemoteSession establishSharedSession(String name) {
- logger.debug("Shared session requested by client. Group name: " + name);
-
- if (name == null) {
- throw new CayenneRuntimeException("Invalid null shared session name");
- }
-
- return createServerSession(name).getSession();
- }
-
- @Override
- public Object processMessage(ClientMessage message) throws Throwable {
-
- if (message == null) {
- throw new IllegalArgumentException("Null client message.");
- }
-
- ServerSession handler = getServerSession();
-
- if (handler == null) {
- throw new MissingSessionException("No session associated with request.");
- }
-
- logger.debug("processMessage, sessionId: " + handler.getSession().getSessionId());
-
- // intercept and log exceptions
- try {
- return DispatchHelper.dispatch(handler.getChannel(), message);
- } catch (Throwable th) {
-
- StringBuilder wrapperMessage = new StringBuilder();
- wrapperMessage.append("Exception processing message ").append(message.getClass().getName())
- .append(" of type ").append(message);
-
- String wrapperMessageString = wrapperMessage.toString();
- logger.info(wrapperMessageString, th);
-
- // This exception will probably be propagated to the client.
- // Recast the exception to a serializable form.
- Exception cause = new Exception(Util.unwindException(th).getLocalizedMessage());
-
- throw new CayenneRuntimeException(wrapperMessageString, cause);
- }
- }
-
- @Override
- public void close() throws RemoteException {
- }
-
- protected RemoteSession createRemoteSession(String sessionId, String name, boolean enableEvents) {
- RemoteSession session = (enableEvents) ? new RemoteSession(sessionId, eventBridgeFactoryName,
- eventBridgeParameters) : new RemoteSession(sessionId);
-
- session.setName(name);
- return session;
- }
-
- /**
- * Creates a server-side channel that will handle all client requests. For
- * shared sessions the same channel instance is reused for the entire group
- * of clients. For dedicated sessions, one channel per client is created.
- * <p>
- * This implementation returns {@link ClientServerChannel} instance wrapping
- * a DataContext. Subclasses may override the method to customize channel
- * creation. For instance they may wrap channel in the custom interceptors
- * to handle transactions or security.
- */
- protected DataChannel createChannel() {
- return new ClientServerChannel((DataContext) contextFactory.createContext());
- }
-
- /**
- * Initializes EventBridge parameters for remote clients peer-to-peer
- * communications.
- */
- protected void initEventBridgeParameters(Map<String, String> properties) {
- String eventBridgeFactoryName = properties.get(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY);
-
- if (eventBridgeFactoryName != null) {
-
- Map<String, String> eventBridgeParameters = new HashMap<>(properties);
- eventBridgeParameters.remove(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY);
-
- this.eventBridgeFactoryName = eventBridgeFactoryName;
- this.eventBridgeParameters = eventBridgeParameters;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
deleted file mode 100644
index 83b23e2..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/DispatchHelper.java
+++ /dev/null
@@ -1,49 +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.remote.service;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.remote.BootstrapMessage;
-import org.apache.cayenne.remote.ClientMessage;
-import org.apache.cayenne.remote.QueryMessage;
-import org.apache.cayenne.remote.SyncMessage;
-
-/**
- * A helper class to match message types with DataChannel methods.
- *
- * @since 1.2
- */
-class DispatchHelper {
-
- static Object dispatch(DataChannel channel, ClientMessage message) {
- // do most common messages first...
- if (message instanceof QueryMessage) {
- return channel.onQuery(null, ((QueryMessage) message).getQuery());
- } else if (message instanceof SyncMessage) {
- SyncMessage sync = (SyncMessage) message;
- return channel.onSync(null, sync.getSenderChanges(), sync.getType());
- } else if (message instanceof BootstrapMessage) {
- return channel.getEntityResolver().getClientEntityResolver();
- } else {
- throw new CayenneRuntimeException("Message dispatch error. Unsupported message: %s", message);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
deleted file mode 100644
index 80bd9bd..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/HttpRemoteService.java
+++ /dev/null
@@ -1,134 +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.remote.service;
-
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.http.HttpSession;
-
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.configuration.ObjectContextFactory;
-import org.apache.cayenne.remote.RemoteSession;
-
-/**
- * A {@link org.apache.cayenne.remote.RemoteService} implementation that stores server
- * context information in HTTP sessions.
- *
- * @since 1.2
- */
-public abstract class HttpRemoteService extends BaseRemoteService {
-
- static final String SESSION_ATTRIBUTE = HttpRemoteService.class.getName()
- + ".ServerSession";
-
- private Map<String, WeakReference<DataChannel>> sharedChannels;
-
- /**
- * @since 3.1
- */
- public HttpRemoteService(ObjectContextFactory contextFactory,
- Map<String, String> eventBridgeProperties) {
- super(contextFactory, eventBridgeProperties);
- this.sharedChannels = new HashMap<>();
- }
-
- /**
- * Returns an HttpSession associated with the current request in progress.
- */
- protected abstract HttpSession getSession(boolean create);
-
- /**
- * Returns a ServerSession object that represents Cayenne-related state associated
- * with the current session. If ServerSession hasn't been previously saved, returns
- * null.
- */
- @Override
- protected ServerSession getServerSession() {
- HttpSession httpSession = getSession(true);
- return (ServerSession) httpSession.getAttribute(SESSION_ATTRIBUTE);
- }
-
- /**
- * Creates a new ServerSession with a dedicated DataChannel. Returned ServerSession is
- * stored in HttpSession for future reuse.
- */
- @Override
- protected ServerSession createServerSession() {
-
- HttpSession httpSession = getSession(true);
-
- DataChannel channel = createChannel();
- RemoteSession remoteSession = createRemoteSession(
- httpSession.getId(),
- null,
- false);
- ServerSession serverSession = new ServerSession(remoteSession, channel);
-
- httpSession.setAttribute(SESSION_ATTRIBUTE, serverSession);
- return serverSession;
- }
-
- /**
- * Creates a new ServerSession based on a shared DataChannel. Returned ServerSession
- * is stored in HttpSession for future reuse.
- *
- * @param name shared session name used to lookup a shared DataChannel.
- */
- @Override
- protected ServerSession createServerSession(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Name is null for shared session.");
- }
-
- HttpSession httpSession = getSession(true);
- DataChannel channel;
-
- synchronized (sharedChannels) {
- channel = getSharedChannel(name);
- if (channel == null) {
- channel = createChannel();
- saveSharedChannel(name, channel);
- logger.debug("Starting a new shared channel: " + name);
- }
- else {
- logger.debug("Joining existing shared channel: " + name);
- }
- }
-
- RemoteSession remoteSession = createRemoteSession(httpSession.getId(), name, true);
-
- ServerSession serverSession = new ServerSession(remoteSession, channel);
- httpSession.setAttribute(SESSION_ATTRIBUTE, serverSession);
- return serverSession;
- }
-
- protected DataChannel getSharedChannel(String name) {
- WeakReference<DataChannel> ref = sharedChannels.get(name);
- return (ref != null) ? ref.get() : null;
- }
-
- protected void saveSharedChannel(String name, DataChannel channel) {
- // wrap value in a WeakReference so that channels can be deallocated when all
- // sessions that reference this channel time out...
- sharedChannels.put(name, new WeakReference<DataChannel>(channel));
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.java
deleted file mode 100644
index e46e50b..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/MissingSessionException.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.remote.service;
-
-import org.apache.cayenne.CayenneRuntimeException;
-
-/**
- * An exception that are thrown by the ROP server if the client are missing a session.
- *
- * @since 3.0
- */
-public class MissingSessionException extends CayenneRuntimeException {
-
- public MissingSessionException() {
- super();
- }
-
- public MissingSessionException(String messageFormat, Object... messageArgs) {
- super(messageFormat, messageArgs);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
deleted file mode 100644
index 2ae8904..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/ServerSession.java
+++ /dev/null
@@ -1,49 +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.remote.service;
-
-import java.io.Serializable;
-
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.remote.RemoteSession;
-
-/**
- * An object that stores server side objects for the client session.
- *
- * @since 1.2
- */
-public class ServerSession implements Serializable {
-
- protected RemoteSession session;
- protected DataChannel channel;
-
- public ServerSession(RemoteSession session, DataChannel channel) {
- this.session = session;
- this.channel = channel;
- }
-
- public DataChannel getChannel() {
- return channel;
- }
-
- public RemoteSession getSession() {
- return session;
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java b/cayenne-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
deleted file mode 100644
index 1adfb52..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/CayenneContextGraphManagerTest.java
+++ /dev/null
@@ -1,67 +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;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertSame;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-/**
- * @since 4.0
- */
-public class CayenneContextGraphManagerTest {
-
- private CayenneContextGraphManager graphManager;
-
- @Before
- public void before() {
- CayenneContext mockContext = mock(CayenneContext.class);
- this.graphManager = new CayenneContextGraphManager(mockContext, false, false);
- }
-
- @Test
- public void testRegisterNode() {
-
- ObjectId id = new ObjectId("E1", "ID", 500);
- Persistent object = mock(Persistent.class);
-
- graphManager.registerNode(id, object);
- assertSame(object, graphManager.getNode(id));
- }
-
- @Test
- public void testUnregisterNode() {
-
- ObjectId id = new ObjectId("E1", "ID", 500);
- Persistent object = mock(Persistent.class);
-
- graphManager.registerNode(id, object);
- Object unregistered = graphManager.unregisterNode(id);
- assertSame(object, unregistered);
-
- verify(object, times(0)).setObjectId(null);
- verify(object).setObjectContext(null);
- verify(object).setPersistenceState(PersistenceState.TRANSIENT);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
deleted file mode 100644
index f1ecab2..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
+++ /dev/null
@@ -1,40 +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.remote;
-
-import java.rmi.RemoteException;
-
-public class MockRemoteService implements RemoteService {
-
- public RemoteSession establishSession() throws RemoteException {
- return null;
- }
-
- public RemoteSession establishSharedSession(String name) throws RemoteException {
- return null;
- }
-
- public Object processMessage(ClientMessage message) throws RemoteException, Throwable {
- return null;
- }
-
- @Override
- public void close() throws RemoteException {
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java
deleted file mode 100644
index 869e110..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/RemoteSessionTest.java
+++ /dev/null
@@ -1,58 +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.remote;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class RemoteSessionTest {
-
- @Test
- public void testConstructor1() {
- RemoteSession descriptor = new RemoteSession("abc");
- assertEquals("abc", descriptor.getSessionId());
- assertFalse(descriptor.isServerEventsEnabled());
- }
-
- @Test
- public void testConstructor2() {
- RemoteSession descriptor = new RemoteSession("abc", "factory", null);
- assertEquals("abc", descriptor.getSessionId());
- assertTrue(descriptor.isServerEventsEnabled());
- }
-
- @Test
- public void testHashCode() {
- RemoteSession d1 = new RemoteSession("1");
- RemoteSession d2 = new RemoteSession("1");
-
- assertEquals(d1.hashCode(), d1.hashCode());
- assertEquals(d1.hashCode(), d2.hashCode());
-
- d2.setName("some name");
- assertEquals(d1.hashCode(), d2.hashCode());
-
- RemoteSession d3 = new RemoteSession("2");
- assertFalse(d1.hashCode() == d3.hashCode());
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.java
deleted file mode 100644
index 4cf0d0b..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/HessianConfigTest.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.remote.hessian;
-
-import com.caucho.hessian.io.AbstractSerializerFactory;
-import com.caucho.hessian.io.SerializerFactory;
-import org.apache.cayenne.map.EntityResolver;
-import org.junit.Test;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-public class HessianConfigTest {
-
- @Test
- public void testLoadFactoryNoExtensions() {
- SerializerFactory factory = HessianConfig.createFactory(null, null);
- assertNotNull(factory);
- }
-
- @Test
- public void testLoadFactoryNoInjection() throws Exception {
- AbstractSerializerFactory factory = HessianConfig.loadFactory(
- MockAbstractSerializerFactory.class.getName(),
- null);
-
- assertTrue(factory instanceof MockAbstractSerializerFactory);
- assertNull(((MockAbstractSerializerFactory) factory).getEntityResolver());
- }
-
- @Test
- public void testLoadFactoryInjection() throws Exception {
- EntityResolver resolver = new EntityResolver();
- AbstractSerializerFactory factory = HessianConfig.loadFactory(
- MockAbstractSerializerFactory.class.getName(),
- resolver);
-
- assertTrue(factory instanceof MockAbstractSerializerFactory);
- assertSame(resolver, ((MockAbstractSerializerFactory) factory)
- .getEntityResolver());
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
deleted file mode 100644
index 8e861ab..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/MockAbstractSerializerFactory.java
+++ /dev/null
@@ -1,51 +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.remote.hessian;
-
-import org.apache.cayenne.map.EntityResolver;
-
-import com.caucho.hessian.io.AbstractSerializerFactory;
-import com.caucho.hessian.io.Deserializer;
-import com.caucho.hessian.io.HessianProtocolException;
-import com.caucho.hessian.io.Serializer;
-
-public class MockAbstractSerializerFactory extends AbstractSerializerFactory {
-
- protected EntityResolver entityResolver;
-
- @Override
- public Serializer getSerializer(Class cl) throws HessianProtocolException {
- return null;
- }
-
- @Override
- public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
- return null;
- }
-
- public EntityResolver getEntityResolver() {
- return entityResolver;
- }
-
- public void setEntityResolver(EntityResolver entityResolver) {
- this.entityResolver = entityResolver;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
deleted file mode 100644
index 590651d..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/hessian/service/HessianServiceTest.java
+++ /dev/null
@@ -1,73 +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.remote.hessian.service;
-
-import com.caucho.services.server.ServiceContext;
-import com.mockrunner.mock.web.MockHttpServletRequest;
-import com.mockrunner.mock.web.MockHttpSession;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.ObjectContextFactory;
-import org.apache.cayenne.event.MockEventBridgeFactory;
-import org.junit.Test;
-
-import javax.servlet.http.HttpSession;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertSame;
-
-public class HessianServiceTest {
-
- @Test
- public void testGetSession() throws Exception {
-
- Map<String, String> map = new HashMap<>();
- map.put(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY, MockEventBridgeFactory.class.getName());
-
- ObjectContextFactory factory = new ObjectContextFactory() {
-
- public ObjectContext createContext(DataChannel parent) {
- return null;
- }
-
- public ObjectContext createContext() {
- return null;
- }
- };
- HessianService service = new HessianService(factory, map);
-
- MockHttpServletRequest request = new MockHttpServletRequest();
- HttpSession session = new MockHttpSession();
- request.setSession(session);
-
- // for some reason need to call this to get session activated in the
- // mock request
- request.getSession();
-
- try {
- ServiceContext.begin(request, null, null, null);
- assertSame(session, service.getSession(false));
- } finally {
- ServiceContext.end();
- }
- }
-}
[3/5] cayenne git commit: CAY-2373 cayenne-rop-server module - move
org.apache.cayenne.remote package to cayenne-rop server module - remove
dependencies from cayenne-server pom.xml - update tutorial
Posted by nt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
new file mode 100644
index 0000000..00feca6
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/BaseRemoteServiceTest.java
@@ -0,0 +1,142 @@
+/*****************************************************************
+ * 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.remote.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataChannel;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.ObjectContextFactory;
+import org.apache.cayenne.event.MockEventBridgeFactory;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.remote.QueryMessage;
+import org.apache.cayenne.remote.RemoteSession;
+import org.apache.cayenne.util.Util;
+import org.junit.Test;
+
+public class BaseRemoteServiceTest {
+
+ @Test
+ public void testConstructor() throws Exception {
+
+ Map<String, String> map = new HashMap<>();
+ map.put(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY, MockEventBridgeFactory.class.getName());
+
+ ObjectContextFactory factory = new ObjectContextFactory() {
+
+ public ObjectContext createContext(DataChannel parent) {
+ return null;
+ }
+
+ public ObjectContext createContext() {
+ return null;
+ }
+ };
+ BaseRemoteService service = new BaseRemoteService(factory, map) {
+
+ @Override
+ protected ServerSession createServerSession() {
+ return null;
+ }
+
+ @Override
+ protected ServerSession createServerSession(String name) {
+ return null;
+ }
+
+ @Override
+ protected ServerSession getServerSession() {
+ return null;
+ }
+ };
+ assertEquals(MockEventBridgeFactory.class.getName(), service.getEventBridgeFactoryName());
+ assertSame(factory, service.contextFactory);
+
+ }
+
+ @Test
+ public void testProcessMessageExceptionSerializability() throws Throwable {
+
+ Map<String, String> map = new HashMap<>();
+ ObjectContextFactory factory = new ObjectContextFactory() {
+
+ public ObjectContext createContext(DataChannel parent) {
+ return null;
+ }
+
+ public ObjectContext createContext() {
+ return null;
+ }
+ };
+ BaseRemoteService service = new BaseRemoteService(factory, map) {
+
+ @Override
+ protected ServerSession createServerSession() {
+ return new ServerSession(new RemoteSession("a"), null);
+ }
+
+ @Override
+ protected ServerSession createServerSession(String name) {
+ return createServerSession();
+ }
+
+ @Override
+ protected ServerSession getServerSession() {
+ return createServerSession();
+ }
+ };
+
+ try {
+ service.processMessage(new QueryMessage(null) {
+
+ @Override
+ public Query getQuery() {
+ // serializable exception thrown
+ throw new CayenneRuntimeException();
+ }
+ });
+
+ fail("Expected to throw");
+ } catch (Exception ex) {
+ Util.cloneViaSerialization(ex);
+ }
+
+ try {
+ service.processMessage(new QueryMessage(null) {
+
+ @Override
+ public Query getQuery() {
+ // non-serializable exception thrown
+ throw new MockUnserializableException();
+ }
+ });
+
+ fail("Expected to throw");
+ } catch (Exception ex) {
+ Util.cloneViaSerialization(ex);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
new file mode 100644
index 0000000..e4b01fe
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/DispatchHelperTest.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ * 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.remote.service;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.MockDataChannel;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.remote.BootstrapMessage;
+import org.apache.cayenne.remote.ClientMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class DispatchHelperTest {
+
+ @Test
+ public void testBootstrapMessage() {
+ EntityResolver resolver = new EntityResolver();
+ MockDataChannel channel = new MockDataChannel(resolver);
+ assertSame(resolver.getClientEntityResolver(), DispatchHelper.dispatch(
+ channel,
+ new BootstrapMessage()));
+ }
+
+ @Test
+ public void testUnknownMessage() {
+ try {
+ DispatchHelper.dispatch(new MockDataChannel(), mock(ClientMessage.class));
+ fail("Unknown message must have failed");
+ }
+ catch (CayenneRuntimeException e) {
+ // expected
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
new file mode 100644
index 0000000..e30435b
--- /dev/null
+++ b/cayenne-rop-server/src/test/java/org/apache/cayenne/remote/service/MockUnserializableException.java
@@ -0,0 +1,24 @@
+/*****************************************************************
+ * 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.remote.service;
+
+class MockUnserializableException extends RuntimeException {
+
+ protected Object notSerializableField = new Object();
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/pom.xml b/cayenne-server/pom.xml
index 4b7fb6b..a310a37 100644
--- a/cayenne-server/pom.xml
+++ b/cayenne-server/pom.xml
@@ -32,7 +32,6 @@
<dependencies>
<!-- Compile dependencies -->
-
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -44,31 +43,6 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
- <!-- Optional dependencies... things that might have been placed in submodules... -->
- <dependency>
- <groupId>com.caucho</groupId>
- <artifactId>hessian</artifactId>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>opensymphony</groupId>
- <artifactId>oscache</artifactId>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-core</artifactId>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
<!-- Test dependencies -->
<dependency>
@@ -110,11 +84,6 @@
</exclusions>
</dependency>
<dependency>
- <groupId>com.mockrunner</groupId>
- <artifactId>mockrunner-servlet</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/CayenneContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContext.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneContext.java
deleted file mode 100644
index c352ae0..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContext.java
+++ /dev/null
@@ -1,391 +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;
-
-import org.apache.cayenne.event.EventManager;
-import org.apache.cayenne.graph.CompoundDiff;
-import org.apache.cayenne.graph.GraphDiff;
-import org.apache.cayenne.graph.GraphManager;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.util.EventUtil;
-import org.apache.cayenne.validation.ValidationException;
-import org.apache.cayenne.validation.ValidationResult;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * A default generic implementation of ObjectContext suitable for accessing
- * Cayenne from either an ORM or a client tiers. Communicates with Cayenne via a
- * {@link org.apache.cayenne.DataChannel}.
- *
- * @since 1.2
- */
-public class CayenneContext extends BaseContext {
-
- CayenneContextGraphManager graphManager;
-
- // object that merges "backdoor" changes that come from the channel.
- CayenneContextMergeHandler mergeHandler;
-
- /**
- * Creates a new CayenneContext with no channel and disabled graph events.
- */
- public CayenneContext() {
- this(null);
- }
-
- /**
- * Creates a new CayenneContext, initializing it with a channel instance.
- * CayenneContext created using this constructor WILL NOT broadcast graph
- * change events.
- */
- public CayenneContext(DataChannel channel) {
- this(channel, false, false);
- }
-
- /**
- * Creates a new CayenneContext, initializing it with a channel.
- */
- public CayenneContext(DataChannel channel, boolean changeEventsEnabled, boolean lifecyleEventsEnabled) {
-
- graphManager = new CayenneContextGraphManager(this, changeEventsEnabled, lifecyleEventsEnabled);
-
- if (channel != null) {
- attachToChannel(channel);
- }
- }
-
- /**
- * @since 3.1
- */
- @Override
- protected void attachToChannel(DataChannel channel) {
- super.attachToChannel(channel);
-
- if (mergeHandler != null) {
- mergeHandler.active = false;
- mergeHandler = null;
- }
-
- EventManager eventManager = channel.getEventManager();
- if (eventManager != null) {
- mergeHandler = new CayenneContextMergeHandler(this);
-
- // listen to our channel events...
- // note that we must reset listener on channel switch, as there is
- // no
- // guarantee that a new channel uses the same EventManager.
- EventUtil.listenForChannelEvents(channel, mergeHandler);
- }
- }
-
- /**
- * Returns true if this context posts individual object modification events.
- * Subject used for these events is
- * <code>ObjectContext.GRAPH_CHANGED_SUBJECT</code>.
- */
- public boolean isChangeEventsEnabled() {
- return graphManager.changeEventsEnabled;
- }
-
- /**
- * Returns true if this context posts lifecycle events. Subjects used for
- * these events are
- * <code>ObjectContext.GRAPH_COMMIT_STARTED_SUBJECT, ObjectContext.GRAPH_COMMITTED_SUBJECT,
- * ObjectContext.GRAPH_COMMIT_ABORTED_SUBJECT, ObjectContext.GRAPH_ROLLEDBACK_SUBJECT.</code>
- * .
- */
- public boolean isLifecycleEventsEnabled() {
- return graphManager.lifecycleEventsEnabled;
- }
-
- @Override
- public GraphManager getGraphManager() {
- return graphManager;
- }
-
- CayenneContextGraphManager internalGraphManager() {
- return graphManager;
- }
-
- /**
- * Commits changes to uncommitted objects. First checks if there are changes
- * in this context and if any changes are detected, sends a commit message
- * to remote Cayenne service via an internal instance of CayenneConnector.
- */
- @Override
- public void commitChanges() {
- doCommitChanges(true);
- }
-
- GraphDiff doCommitChanges(boolean cascade) {
-
- int syncType = cascade ? DataChannel.FLUSH_CASCADE_SYNC : DataChannel.FLUSH_NOCASCADE_SYNC;
-
- GraphDiff commitDiff = null;
-
- synchronized (graphManager) {
-
- if (graphManager.hasChanges()) {
-
- if (isValidatingObjectsOnCommit()) {
- ValidationResult result = new ValidationResult();
- Iterator<?> it = graphManager.dirtyNodes().iterator();
- while (it.hasNext()) {
- Persistent p = (Persistent) it.next();
- if (p instanceof Validating) {
- switch (p.getPersistenceState()) {
- case PersistenceState.NEW:
- ((Validating) p).validateForInsert(result);
- break;
- case PersistenceState.MODIFIED:
- ((Validating) p).validateForUpdate(result);
- break;
- case PersistenceState.DELETED:
- ((Validating) p).validateForDelete(result);
- break;
- }
- }
- }
-
- if (result.hasFailures()) {
- throw new ValidationException(result);
- }
- }
-
- graphManager.graphCommitStarted();
-
- GraphDiff changes = graphManager.getDiffsSinceLastFlush();
-
- try {
- commitDiff = channel.onSync(this, changes, syncType);
- } catch (Throwable th) {
- graphManager.graphCommitAborted();
-
- if (th instanceof CayenneRuntimeException) {
- throw (CayenneRuntimeException) th;
- } else {
- throw new CayenneRuntimeException("Commit error", th);
- }
- }
-
- graphManager.graphCommitted(commitDiff);
-
- // this event is caught by peer nested ObjectContexts to
- // synchronize the
- // state
- fireDataChannelCommitted(this, changes);
- }
- }
-
- return commitDiff;
- }
-
- @Override
- public void commitChangesToParent() {
- doCommitChanges(false);
- }
-
- @Override
- public void rollbackChanges() {
- synchronized (graphManager) {
- if (graphManager.hasChanges()) {
-
- GraphDiff diff = graphManager.getDiffs();
- graphManager.graphReverted();
-
- channel.onSync(this, diff, DataChannel.ROLLBACK_CASCADE_SYNC);
- fireDataChannelRolledback(this, diff);
- }
- }
- }
-
- @Override
- public void rollbackChangesLocally() {
- synchronized (graphManager) {
- if (graphManager.hasChanges()) {
- GraphDiff diff = graphManager.getDiffs();
- graphManager.graphReverted();
-
- fireDataChannelRolledback(this, diff);
- }
- }
- }
-
- /**
- * Creates and registers a new Persistent object instance.
- */
- @Override
- public <T> T newObject(Class<T> persistentClass) {
- if (persistentClass == null) {
- throw new NullPointerException("Persistent class can't be null.");
- }
-
- ObjEntity entity = getEntityResolver().getObjEntity(persistentClass);
- if (entity == null) {
- throw new CayenneRuntimeException("No entity mapped for class: %s", persistentClass);
- }
-
- ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
- @SuppressWarnings("unchecked")
- T object = (T) descriptor.createObject();
- descriptor.injectValueHolders(object);
- registerNewObject((Persistent) object, entity.getName(), descriptor);
- return object;
- }
-
- /**
- * @since 3.0
- */
- @Override
- public void registerNewObject(Object object) {
- if (object == null) {
- throw new NullPointerException("An attempt to register null object.");
- }
-
- ObjEntity entity = getEntityResolver().getObjEntity(object.getClass());
- ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
- registerNewObject((Persistent) object, entity.getName(), descriptor);
- }
-
- /**
- * Runs a query, returning result as list.
- */
- @Override
- public List performQuery(Query query) {
- List result = onQuery(this, query).firstList();
- return result != null ? result : new ArrayList<>(1);
- }
-
- @Override
- public QueryResponse performGenericQuery(Query query) {
- return onQuery(this, query);
- }
-
- public QueryResponse onQuery(ObjectContext context, Query query) {
- return new CayenneContextQueryAction(this, context, query).execute();
- }
-
- @Override
- public Collection<?> uncommittedObjects() {
- synchronized (graphManager) {
- return graphManager.dirtyNodes();
- }
- }
-
- @Override
- public Collection<?> deletedObjects() {
- synchronized (graphManager) {
- return graphManager.dirtyNodes(PersistenceState.DELETED);
- }
- }
-
- @Override
- public Collection<?> modifiedObjects() {
- synchronized (graphManager) {
- return graphManager.dirtyNodes(PersistenceState.MODIFIED);
- }
- }
-
- @Override
- public Collection<?> newObjects() {
- synchronized (graphManager) {
- return graphManager.dirtyNodes(PersistenceState.NEW);
- }
- }
-
- // ****** non-public methods ******
-
- void registerNewObject(Persistent object, String entityName, ClassDescriptor descriptor) {
- /**
- * We should create new id only if it is not set for this object. It
- * could have been created, for instance, in child context
- */
- ObjectId id = object.getObjectId();
- if (id == null) {
- id = new ObjectId(entityName);
- object.setObjectId(id);
- }
-
- injectInitialValue(object);
- }
-
- Persistent createFault(ObjectId id) {
- ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
-
- Persistent object = (Persistent) descriptor.createObject();
-
- object.setPersistenceState(PersistenceState.HOLLOW);
- object.setObjectContext(this);
- object.setObjectId(id);
-
- graphManager.registerNode(id, object);
-
- return object;
- }
-
- @Override
- protected GraphDiff onContextFlush(ObjectContext originatingContext, GraphDiff changes, boolean cascade) {
-
- boolean childContext = this != originatingContext && changes != null;
-
- if (childContext) {
-
- // PropertyChangeProcessingStrategy oldStrategy =
- // getPropertyChangeProcessingStrategy();
- // setPropertyChangeProcessingStrategy(PropertyChangeProcessingStrategy.RECORD);
- try {
- changes.apply(new CayenneContextChildDiffLoader(this));
- } finally {
- // setPropertyChangeProcessingStrategy(oldStrategy);
- }
-
- fireDataChannelChanged(originatingContext, changes);
- }
-
- return (cascade) ? doCommitChanges(true) : new CompoundDiff();
- }
-
- /**
- * Returns <code>true</code> if there are any modified, deleted or new
- * objects registered with this CayenneContext, <code>false</code>
- * otherwise.
- */
- public boolean hasChanges() {
- return graphManager.hasChanges();
- }
-
- /**
- * This method simply returns an iterator over a list of selected objects.
- * There's no performance benefit of using it vs. regular "select".
- *
- * @since 4.0
- */
- public <T> ResultIterator<T> iterator(org.apache.cayenne.query.Select<T> query) {
- List<T> objects = select(query);
- return new CollectionResultIterator<T>(objects);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
deleted file mode 100644
index 151b72e..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
+++ /dev/null
@@ -1,142 +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;
-
-import org.apache.cayenne.graph.ChildDiffLoader;
-import org.apache.cayenne.reflect.ArcProperty;
-import org.apache.cayenne.reflect.AttributeProperty;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.reflect.PropertyDescriptor;
-import org.apache.cayenne.reflect.PropertyVisitor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.reflect.ToOneProperty;
-
-/**
- * Used for loading child's CayenneContext changes to parent context.
- *
- * @since 3.0
- */
-class CayenneContextChildDiffLoader extends ChildDiffLoader {
-
- public CayenneContextChildDiffLoader(CayenneContext context) {
- super(context);
- }
-
- @Override
- public void nodePropertyChanged(
- Object nodeId,
- String property,
- Object oldValue,
- Object newValue) {
-
- super.nodePropertyChanged(nodeId, property, oldValue, newValue);
-
- Persistent object = (Persistent) context.getGraphManager().getNode(nodeId);
- context.propertyChanged(object, property, oldValue, newValue);
- }
-
- @Override
- public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
-
- final Persistent source = findObject(nodeId);
- final Persistent target = findObject(targetNodeId);
-
- // if a target was later deleted, the diff for arcCreated is still preserved and
- // can result in NULL target here.
- if (target == null) {
- return;
- }
-
- ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
- ((ObjectId) nodeId).getEntityName());
- ArcProperty property = (ArcProperty) descriptor.getProperty(arcId.toString());
-
- property.visit(new PropertyVisitor() {
-
- public boolean visitAttribute(AttributeProperty property) {
- return false;
- }
-
- public boolean visitToMany(ToManyProperty property) {
- property.addTargetDirectly(source, target);
- return false;
- }
-
- public boolean visitToOne(ToOneProperty property) {
- property.setTarget(source, target, false);
- return false;
- }
- });
- context.propertyChanged(source, (String) arcId, null, target);
- }
-
- @Override
- public void arcDeleted(Object nodeId, final Object targetNodeId, Object arcId) {
- final Persistent source = findObject(nodeId);
-
- // needed as sometime temporary objects are evoked from the context before
- // changing their relationships
- if (source == null) {
- return;
- }
-
- ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
- ((ObjectId) nodeId).getEntityName());
- PropertyDescriptor property = descriptor.getProperty(arcId.toString());
-
- final Persistent[] target = new Persistent[1];
- target[0] = findObject(targetNodeId);
-
- property.visit(new PropertyVisitor() {
-
- public boolean visitAttribute(AttributeProperty property) {
- return false;
- }
-
- public boolean visitToMany(ToManyProperty property) {
- if (target[0] == null) {
-
- // this is usually the case when a NEW object was deleted and then
- // its relationships were manipulated; so try to locate the object
- // in the collection ... the performance of this is rather dubious
- // of course...
- target[0] = findObjectInCollection(targetNodeId, property
- .readProperty(source));
- }
-
- if (target[0] == null) {
- // ignore?
- }
- else {
- property.removeTargetDirectly(source, target[0]);
- }
-
- return false;
- }
-
- public boolean visitToOne(ToOneProperty property) {
- property.setTarget(source, null, false);
- return false;
- }
- });
-
- context.propertyChanged(source, (String) arcId, target[0], null);
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
deleted file mode 100644
index f4865c0..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
+++ /dev/null
@@ -1,379 +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;
-
-import org.apache.cayenne.event.EventManager;
-import org.apache.cayenne.event.EventSubject;
-import org.apache.cayenne.graph.ArcCreateOperation;
-import org.apache.cayenne.graph.ArcDeleteOperation;
-import org.apache.cayenne.graph.GraphChangeHandler;
-import org.apache.cayenne.graph.GraphDiff;
-import org.apache.cayenne.graph.GraphEvent;
-import org.apache.cayenne.graph.GraphMap;
-import org.apache.cayenne.graph.NodeCreateOperation;
-import org.apache.cayenne.graph.NodeDeleteOperation;
-import org.apache.cayenne.graph.NodeIdChangeOperation;
-import org.apache.cayenne.graph.NodePropertyChangeOperation;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.reflect.ArcProperty;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.reflect.PropertyException;
-import org.apache.cayenne.reflect.ToManyMapProperty;
-import org.apache.cayenne.util.PersistentObjectMap;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * A GraphMap extension that works together with {@link ObjectContext} to track persistent object
- * changes and send events.
- *
- * @since 1.2
- */
-final class CayenneContextGraphManager extends GraphMap {
-
- static final String COMMIT_MARKER = "commit";
- static final String FLUSH_MARKER = "flush";
-
- CayenneContext context;
- Collection<Object> deadIds;
- boolean changeEventsEnabled;
- boolean lifecycleEventsEnabled;
-
- ObjectContextStateLog stateLog;
- ObjectContextChangeLog changeLog;
-
- CayenneContextGraphManager(CayenneContext context, boolean changeEventsEnabled,
- boolean lifecycleEventsEnabled) {
-
- this.context = context;
- this.changeEventsEnabled = changeEventsEnabled;
- this.lifecycleEventsEnabled = lifecycleEventsEnabled;
-
- this.stateLog = new ObjectContextStateLog(this);
- this.changeLog = new ObjectContextChangeLog();
- }
-
- boolean hasChanges() {
- return changeLog.size() > 0;
- }
-
- boolean hasChangesSinceLastFlush() {
- int size = changeLog.hasMarker(FLUSH_MARKER) ? changeLog
- .sizeAfterMarker(FLUSH_MARKER) : changeLog.size();
- return size > 0;
- }
-
- GraphDiff getDiffs() {
- return changeLog.getDiffs();
- }
-
- GraphDiff getDiffsSinceLastFlush() {
- return changeLog.hasMarker(FLUSH_MARKER) ? changeLog
- .getDiffsAfterMarker(FLUSH_MARKER) : changeLog.getDiffs();
- }
-
- Collection<Object> dirtyNodes() {
- return stateLog.dirtyNodes();
- }
-
- Collection<Object> dirtyNodes(int state) {
- return stateLog.dirtyNodes(state);
- }
-
- @Override
- public synchronized Object unregisterNode(Object nodeId) {
- Object node = super.unregisterNode(nodeId);
-
- // remove node from other collections...
- if (node != null) {
- stateLog.unregisterNode(nodeId);
- changeLog.unregisterNode(nodeId);
- Persistent object = (Persistent)node;
- object.setObjectContext(null);
- object.setPersistenceState(PersistenceState.TRANSIENT);
- return node;
- }
-
- return null;
- }
-
- // ****** Sync Events API *****
- /**
- * Clears commit marker, but keeps all recorded operations.
- */
- void graphCommitAborted() {
- changeLog.removeMarker(COMMIT_MARKER);
- }
-
- /**
- * Sets commit start marker in the change log. If events are enabled, posts commit
- * start event.
- */
- void graphCommitStarted() {
- changeLog.setMarker(COMMIT_MARKER);
- }
-
- void graphCommitted(GraphDiff parentSyncDiff) {
- if (parentSyncDiff != null) {
- new CayenneContextMergeHandler(context).merge(parentSyncDiff);
- }
-
- remapTargets();
-
- stateLog.graphCommitted();
- reset();
-
- if (lifecycleEventsEnabled) {
- // include all diffs after the commit start marker.
- // We fire event as if it was posted by parent channel, so that
- // nested contexts could catch it
- context.fireDataChannelCommitted(context.getChannel(), parentSyncDiff);
- }
- }
-
- /**
- * Remaps keys in to-many map relationships that contain dirty objects with
- * potentially modified properties.
- */
- private void remapTargets() {
-
- Iterator<Object> it = stateLog.dirtyIds().iterator();
-
- EntityResolver resolver = context.getEntityResolver();
-
- while (it.hasNext()) {
- ObjectId id = (ObjectId) it.next();
- ClassDescriptor descriptor = resolver.getClassDescriptor(id.getEntityName());
-
- Collection<ArcProperty> mapArcProperties = descriptor.getMapArcProperties();
- if (!mapArcProperties.isEmpty()) {
-
- Object object = getNode(id);
-
- for (ArcProperty arc : mapArcProperties) {
- ToManyMapProperty reverseArc = (ToManyMapProperty) arc
- .getComplimentaryReverseArc();
-
- Object source = arc.readPropertyDirectly(object);
- if (source != null && !reverseArc.isFault(source)) {
- remapTarget(reverseArc, source, object);
- }
- }
- }
- }
- }
-
- // clone of DataDomainSyncBucket.remapTarget
- private final void remapTarget(
- ToManyMapProperty property,
- Object source,
- Object target) throws PropertyException {
-
- @SuppressWarnings("unchecked")
- Map<Object, Object> map = (Map<Object, Object>) property.readProperty(source);
-
- Object newKey = property.getMapKey(target);
- Object currentValue = map.get(newKey);
-
- if (currentValue == target) {
- // nothing to do
- return;
- }
- // else - do not check for conflicts here (i.e. another object mapped for the same
- // key), as we have no control of the order in which this method is called, so
- // another object may be remapped later by the caller
-
- // must do a slow map scan to ensure the object is not mapped under a different
- // key...
- Iterator<?> it = map.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<?, ?> e = (Map.Entry<?, ?>) it.next();
- if (e.getValue() == target) {
- // this remove does not trigger event in PersistentObjectMap
- it.remove();
- break;
- }
- }
-
- // TODO: (andrey, 25/11/09 - this is a hack to prevent event triggering
- // (and concurrent exceptions)
- // should find a way to get rid of type casting
- ((PersistentObjectMap) map).putDirectly(newKey, target);
- }
-
- void graphFlushed() {
- changeLog.setMarker(FLUSH_MARKER);
- }
-
- void graphReverted() {
- GraphDiff diff = changeLog.getDiffs();
-
- diff.undo(new RollbackChangeHandler());
- stateLog.graphReverted();
- reset();
-
- if (lifecycleEventsEnabled) {
- context.fireDataChannelRolledback(context, diff);
- }
- }
-
- // ****** GraphChangeHandler API ******
- // =====================================================
-
- @Override
- public synchronized void nodeIdChanged(Object nodeId, Object newId) {
- stateLog.nodeIdChanged(nodeId, newId);
- processChange(new NodeIdChangeOperation(nodeId, newId));
- }
-
- @Override
- public synchronized void nodeCreated(Object nodeId) {
- stateLog.nodeCreated(nodeId);
- processChange(new NodeCreateOperation(nodeId));
- }
-
- @Override
- public synchronized void nodeRemoved(Object nodeId) {
- stateLog.nodeRemoved(nodeId);
- processChange(new NodeDeleteOperation(nodeId));
- }
-
- @Override
- public synchronized void nodePropertyChanged(
- Object nodeId,
- String property,
- Object oldValue,
- Object newValue) {
-
- stateLog.nodePropertyChanged(nodeId, property, oldValue, newValue);
- processChange(new NodePropertyChangeOperation(
- nodeId,
- property,
- oldValue,
- newValue));
- }
-
- @Override
- public synchronized void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
- stateLog.arcCreated(nodeId, targetNodeId, arcId);
- processChange(new ArcCreateOperation(nodeId, targetNodeId, arcId));
- }
-
- @Override
- public synchronized void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
- stateLog.arcDeleted(nodeId, targetNodeId, arcId);
- processChange(new ArcDeleteOperation(nodeId, targetNodeId, arcId));
- }
-
- // ****** helper methods ******
- // =====================================================
-
- private void processChange(GraphDiff diff) {
- changeLog.addOperation(diff);
-
- if (changeEventsEnabled) {
- context.fireDataChannelChanged(context, diff);
- }
- }
-
- /**
- * Wraps GraphDiff in a GraphEvent and sends it via EventManager with specified
- * subject.
- */
- void send(GraphDiff diff, EventSubject subject, Object eventSource) {
- EventManager manager = (context.getChannel() != null) ? context
- .getChannel()
- .getEventManager() : null;
-
- if (manager != null) {
- GraphEvent e = new GraphEvent(context, eventSource, diff);
- manager.postEvent(e, subject);
- }
- }
-
- void reset() {
- changeLog.reset();
-
- if (deadIds != null) {
- // unregister dead ids...
- for (final Object deadId : deadIds) {
- nodes.remove(deadId);
- }
-
- deadIds = null;
- }
- }
-
- Collection<Object> deadIds() {
- if (deadIds == null) {
- deadIds = new ArrayList<>();
- }
-
- return deadIds;
- }
-
- /**
- * This change handler is used to perform rollback actions for Cayenne context
- */
- class RollbackChangeHandler implements GraphChangeHandler {
-
- public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
- context.mergeHandler.arcCreated(nodeId, targetNodeId, arcId);
- CayenneContextGraphManager.this.arcCreated(nodeId, targetNodeId, arcId);
- }
-
- public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
- context.mergeHandler.arcDeleted(nodeId, targetNodeId, arcId);
- CayenneContextGraphManager.this.arcDeleted(nodeId, targetNodeId, arcId);
- }
-
- public void nodeCreated(Object nodeId) {
- CayenneContextGraphManager.this.nodeCreated(nodeId);
- }
-
- public void nodeIdChanged(Object nodeId, Object newId) {
- CayenneContextGraphManager.this.nodeIdChanged(nodeId, newId);
- }
-
- /**
- * Need to write property directly to this context
- */
- public void nodePropertyChanged(
- Object nodeId,
- String property,
- Object oldValue,
- Object newValue) {
- context.mergeHandler
- .nodePropertyChanged(nodeId, property, oldValue, newValue);
- CayenneContextGraphManager.this.nodePropertyChanged(
- nodeId,
- property,
- oldValue,
- newValue);
- }
-
- public void nodeRemoved(Object nodeId) {
- CayenneContextGraphManager.this.nodeRemoved(nodeId);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
deleted file mode 100644
index f490128..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
+++ /dev/null
@@ -1,274 +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;
-
-import org.apache.cayenne.graph.GraphChangeHandler;
-import org.apache.cayenne.graph.GraphDiff;
-import org.apache.cayenne.graph.GraphEvent;
-import org.apache.cayenne.reflect.ArcProperty;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.reflect.PropertyDescriptor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.util.Util;
-
-/**
- * An object that merges "backdoor" modifications of the object graph coming from the
- * underlying DataChannel. When doing an update, CayenneContextMergeHandler blocks
- * broadcasting of GraphManager events.
- *
- * @since 1.2
- */
-class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListener {
-
- CayenneContext context;
- boolean active;
-
- CayenneContextMergeHandler(CayenneContext context) {
- this.context = context;
- this.active = true;
- }
-
- // ******* DataChannelListener methods *******
-
- public void graphChanged(final GraphEvent e) {
- // process flush
- if (shouldProcessEvent(e) && e.getDiff() != null) {
- runWithEventsDisabled(new Runnable() {
-
- public void run() {
- e.getDiff().apply(CayenneContextMergeHandler.this);
-
- }
- });
-
- // post event outside of "execute" to make sure it is sent
- repostAfterMerge(e);
- }
- }
-
- public void graphFlushed(final GraphEvent e) {
- // TODO (Andrus, 10/17/2005) - there are a few problems with commit processing:
-
- // 1. Event mechanism reliability:
- // - events may come out of order (commit and then preceeding flush)
- // - events may be missing all together (commit arrived, while prior flush did
- // not)
- // Possible solution - an "event_version_id" to be used for optimistic locking
-
- // 2. We don't know if our own dirty objects were committed or not...
- // For now we will simply merge the changes, and keep the context dirty
-
- if (shouldProcessEvent(e)) {
-
- runWithEventsDisabled(new Runnable() {
-
- public void run() {
-
- if (e.getDiff() != null) {
- e.getDiff().apply(CayenneContextMergeHandler.this);
- }
- }
- });
-
- // post event outside of "execute" to make sure it is sent
- repostAfterMerge(e);
- }
- }
-
- public void graphRolledback(final GraphEvent e) {
-
- // TODO: andrus, 3/29/2007: per CAY-771, if a LOCAL peer context posted the event,
- // just ignore it, however if the REMOTE peer reverted the parent remote
- // DataContext, we need to invalidate stale committed objects...
- }
-
- // ******* End DataChannelListener methods *******
-
- void repostAfterMerge(GraphEvent originalEvent) {
- // though the subject is CHANGE, "merge" events are really lifecycle.
- if (context.isLifecycleEventsEnabled()) {
- context.fireDataChannelChanged(originalEvent.getSource(), originalEvent.getDiff());
- }
- }
-
- /**
- * Executes merging of the external diff.
- */
- void merge(final GraphDiff diff) {
- runWithEventsDisabled(new Runnable() {
-
- public void run() {
- diff.apply(CayenneContextMergeHandler.this);
- }
- });
- }
-
- // ******* GraphChangeHandler methods *********
-
- public void nodeIdChanged(Object nodeId, Object newId) {
- // do not unregister the node just yet... only put replaced id in deadIds to
- // remove it later. Otherwise stored operations will not work
- Object node = context.internalGraphManager().getNode(nodeId);
-
- if (node != null) {
- context.internalGraphManager().deadIds().add(nodeId);
- context.internalGraphManager().registerNode(newId, node);
-
- if (node instanceof Persistent) {
- // inject new id
- ((Persistent) node).setObjectId((ObjectId) newId);
- }
- }
- }
-
- public void nodeCreated(Object nodeId) {
- // ignore
- }
-
- public void nodeRemoved(Object nodeId) {
- context.getGraphManager().unregisterNode(nodeId);
- }
-
- public void nodePropertyChanged(
- Object nodeId,
- String property,
- Object oldValue,
- Object newValue) {
-
- Object object = context.internalGraphManager().getNode(nodeId);
- if (object != null) {
-
- // do not override local changes....
- PropertyDescriptor p = propertyForId(nodeId, property);
- if (Util.nullSafeEquals(p.readPropertyDirectly(object), oldValue)) {
-
- p.writePropertyDirectly(object, oldValue, newValue);
- }
- }
- }
-
- public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
- // null source or target likely means the object is not faulted yet... Faults
- // shouldn't get disturbed by adding/removing arcs
-
- Object source = context.internalGraphManager().getNode(nodeId);
- if (source == null) {
- // no need to connect non-existent object
- return;
- }
-
- // TODO (Andrus, 10/17/2005) - check for local modifications to avoid
- // overwriting...
-
- ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
- if (p.isFault(source)) {
- return;
- }
-
- Object target = context.internalGraphManager().getNode(targetNodeId);
- if (target == null) {
- target = context.createFault((ObjectId) targetNodeId);
- }
-
- try {
- if (p instanceof ToManyProperty) {
- ((ToManyProperty) p).addTargetDirectly(source, target);
- }
- else {
- p.writePropertyDirectly(source, null, target);
- }
- }
- finally {
- }
- }
-
- public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
-
- // null source or target likely means the object is not faulted yet... Faults
- // shouldn't get disturbed by adding/removing arcs
-
- Object source = context.internalGraphManager().getNode(nodeId);
- if (source == null) {
- // no need to disconnect non-existent object
- return;
- }
-
- // (see "TODO" in 'arcCreated')
- ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
- if (p.isFault(source)) {
- return;
- }
-
- Object target = context.internalGraphManager().getNode(targetNodeId);
- if (target == null) {
- target = context.createFault((ObjectId) targetNodeId);
- }
-
- try {
- if (p instanceof ToManyProperty) {
- ((ToManyProperty) p).removeTargetDirectly(source, target);
- }
- else {
- p.writePropertyDirectly(source, target, null);
- }
- }
- finally {
- }
- }
-
- private PropertyDescriptor propertyForId(Object nodeId, String propertyName) {
- ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
- ((ObjectId) nodeId).getEntityName());
- return descriptor.getProperty(propertyName);
- }
-
- // Returns true if this object is active; an event came from our channel, but did not
- // originate in it.
- boolean shouldProcessEvent(GraphEvent e) {
- // only process events that came from our channel, but did not originate in it
- // (i.e. likely posted by EventBridge)
- return active
- && e.getSource() == context.getChannel()
- && e.getPostedBy() != context
- && e.getPostedBy() != context.getChannel();
- }
-
- // executes a closure, disabling ObjectContext events for the duration of the
- // execution.
-
- private void runWithEventsDisabled(Runnable closure) {
-
- synchronized (context.internalGraphManager()) {
- boolean changeEventsEnabled = context.internalGraphManager().changeEventsEnabled;
- context.internalGraphManager().changeEventsEnabled = false;
-
- boolean lifecycleEventsEnabled = context.internalGraphManager().lifecycleEventsEnabled;
- context.internalGraphManager().lifecycleEventsEnabled = false;
-
- try {
- closure.run();
- }
- finally {
- context.internalGraphManager().changeEventsEnabled = changeEventsEnabled;
- context.internalGraphManager().lifecycleEventsEnabled = lifecycleEventsEnabled;
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java b/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
deleted file mode 100644
index e0b693c..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
+++ /dev/null
@@ -1,186 +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;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.cayenne.cache.QueryCacheEntryFactory;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.RefreshQuery;
-import org.apache.cayenne.reflect.AttributeProperty;
-import org.apache.cayenne.reflect.ClassDescriptor;
-import org.apache.cayenne.reflect.PropertyVisitor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.reflect.ToOneProperty;
-import org.apache.cayenne.remote.RemoteIncrementalFaultList;
-import org.apache.cayenne.util.ListResponse;
-import org.apache.cayenne.util.ObjectContextQueryAction;
-
-/**
- * @since 1.2
- */
-class CayenneContextQueryAction extends ObjectContextQueryAction {
-
- CayenneContextQueryAction(CayenneContext actingContext, ObjectContext targetContext,
- Query query) {
- super(actingContext, targetContext, query);
- }
-
- @Override
- protected boolean interceptPaginatedQuery() {
- if (metadata.getPageSize() > 0) {
- response = new ListResponse(new RemoteIncrementalFaultList(
- actingContext,
- query));
- return DONE;
- }
-
- return !DONE;
- }
-
- @Override
- protected QueryCacheEntryFactory getCacheObjectFactory() {
- return new QueryCacheEntryFactory() {
-
- public List createObject() {
- if (interceptPaginatedQuery() != DONE) {
- runQuery();
- }
- return response.firstList();
- }
- };
- }
-
- @Override
- protected boolean interceptRefreshQuery() {
- if (query instanceof RefreshQuery) {
- RefreshQuery refreshQuery = (RefreshQuery) query;
-
- CayenneContext context = (CayenneContext) actingContext;
-
- // handle 4 separate scenarios, but do not combine them as it will be
- // unclear how to handle cascading behavior
-
- // 1. refresh all
- if (refreshQuery.isRefreshAll()) {
-
- invalidateLocally(context.internalGraphManager(), context
- .internalGraphManager()
- .registeredNodes()
- .iterator());
- context.getQueryCache().clear();
-
- // cascade
- return !DONE;
- }
-
- // 2. invalidate object collection
- Collection<?> objects = refreshQuery.getObjects();
- if (objects != null && !objects.isEmpty()) {
-
- invalidateLocally(context.internalGraphManager(), objects.iterator());
-
- // cascade
- return !DONE;
- }
-
- // 3. refresh query - have to do it eagerly to refresh the objects involved
- if (refreshQuery.getQuery() != null) {
- Query cachedQuery = refreshQuery.getQuery();
-
- String cacheKey = cachedQuery
- .getMetaData(context.getEntityResolver())
- .getCacheKey();
- context.getQueryCache().remove(cacheKey);
-
- this.response = context.performGenericQuery(cachedQuery);
-
- // do not cascade to avoid running query twice
- return DONE;
- }
-
- // 4. refresh groups...
- if (refreshQuery.getGroupKeys() != null
- && refreshQuery.getGroupKeys().length > 0) {
-
- String[] groups = refreshQuery.getGroupKeys();
- for (String group : groups) {
- context.getQueryCache().removeGroup(group);
- }
-
- // cascade group invalidation
- return !DONE;
- }
- }
-
- return !DONE;
- }
-
- private void invalidateLocally(CayenneContextGraphManager graphManager, Iterator<?> it) {
- if (!it.hasNext()) {
- return;
- }
-
- EntityResolver resolver = actingContext.getEntityResolver();
-
- while (it.hasNext()) {
- final Persistent object = (Persistent) it.next();
-
- // we don't care about NEW objects,
- // but we still do care about HOLLOW, since snapshot might still be
- // present
- if (object.getPersistenceState() == PersistenceState.NEW) {
- continue;
- }
-
- ObjectId id = object.getObjectId();
-
- // per CAY-1082 ROP objects (unlike CayenneDataObject) require all
- // relationship faults invalidation.
- ClassDescriptor descriptor = resolver.getClassDescriptor(id.getEntityName());
- PropertyVisitor arcInvalidator = new PropertyVisitor() {
-
- public boolean visitAttribute(AttributeProperty property) {
- return true;
- }
-
- public boolean visitToMany(ToManyProperty property) {
- property.invalidate(object);
- return true;
- }
-
- public boolean visitToOne(ToOneProperty property) {
- property.invalidate(object);
- return true;
- }
- };
-
- descriptor.visitProperties(arcInvalidator);
- object.setPersistenceState(PersistenceState.HOLLOW);
-
- // remove cached changes
- graphManager.changeLog.unregisterNode(id);
- graphManager.stateLog.unregisterNode(id);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
deleted file mode 100644
index 26f2f1b..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
+++ /dev/null
@@ -1,36 +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.remote;
-
-/**
- * A message sent to a remote service to request Cayenne mapping info.
- *
- * @since 1.2
- */
-public class BootstrapMessage implements ClientMessage {
-
- /**
- * Returns a description of the type of message. In this case always "Bootstrap".
- */
- @Override
- public String toString() {
- return "Bootstrap";
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
deleted file mode 100644
index e0740b3..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
+++ /dev/null
@@ -1,31 +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.remote;
-
-import java.io.Serializable;
-
-/**
- * A tag interface representing a message sent by a remote client to Cayenne service.
- *
- * @since 1.2
- */
-public interface ClientMessage extends Serializable {
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
deleted file mode 100644
index 51c7946..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
+++ /dev/null
@@ -1,73 +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.remote;
-
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.QueryMetadataProxy;
-import org.apache.cayenne.query.QueryRouter;
-import org.apache.cayenne.query.SQLAction;
-import org.apache.cayenne.query.SQLActionVisitor;
-
-/**
- * A client wrapper for the incremental query that overrides the metadata to ensure that
- * query result is cached on the server, so that subranges could be retrieved at a later
- * time.
- *
- * @since 1.2
- */
-class IncrementalQuery implements Query {
-
- private Query query;
- private String cacheKey;
-
- IncrementalQuery(Query query, String cacheKey) {
- this.query = query;
- this.cacheKey = cacheKey;
- }
-
- public QueryMetadata getMetaData(EntityResolver resolver) {
- final QueryMetadata metadata = query.getMetaData(resolver);
-
- // the way paginated queries work on the server is that they are never cached
- // (IncrementalFaultList interception happens before cache interception). So
- // overriding caching settings in the metadata will only affect
- // ClientServerChannel behavior
- return new QueryMetadataProxy(metadata) {
- public Query getOriginatingQuery() {
- return null;
- }
-
- public String getCacheKey() {
- return cacheKey;
- }
- };
- }
-
- public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
- query.route(router, resolver, substitutedQuery);
- }
-
- public SQLAction createSQLAction(SQLActionVisitor visitor) {
- return query.createSQLAction(visitor);
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
deleted file mode 100644
index 9b6f563..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
+++ /dev/null
@@ -1,300 +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.remote;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.ResultBatchIterator;
-import org.apache.cayenne.ResultIterator;
-import org.apache.cayenne.ResultIteratorCallback;
-import org.apache.cayenne.access.IncrementalFaultList;
-import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.query.Ordering;
-import org.apache.cayenne.query.PrefetchTreeNode;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.QueryMetadataProxy;
-import org.apache.cayenne.query.QueryRouter;
-import org.apache.cayenne.query.SQLAction;
-import org.apache.cayenne.query.SQLActionVisitor;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.query.SortOrder;
-import org.apache.cayenne.util.XMLEncoder;
-
-/**
- * A SelectQuery decorator that overrides the metadata to ensure that query
- * result is cached on the server, so that subranges could be retrieved at a
- * later time. Note that a special decorator that is a subclass of SelectQuery
- * is needed so that {@link IncrementalFaultList} on the server-side could apply
- * SelectQuery-specific optimizations.
- *
- * @since 3.0
- */
-class IncrementalSelectQuery<T> extends SelectQuery<T> {
-
- private SelectQuery<T> query;
- private String cacheKey;
-
- IncrementalSelectQuery(SelectQuery<T> delegate, String cacheKey) {
- this.query = delegate;
- this.cacheKey = cacheKey;
- }
-
- @Override
- public QueryMetadata getMetaData(EntityResolver resolver) {
- final QueryMetadata metadata = query.getMetaData(resolver);
-
- // the way paginated queries work on the server is that they are never
- // cached
- // (IncrementalFaultList interception happens before cache
- // interception). So
- // overriding caching settings in the metadata will only affect
- // ClientServerChannel behavior
- return new QueryMetadataProxy(metadata) {
- public Query getOriginatingQuery() {
- return null;
- }
-
- public String getCacheKey() {
- return cacheKey;
- }
- };
- }
-
- @Override
- public void addOrdering(Ordering ordering) {
- query.addOrdering(ordering);
- }
-
- @Override
- public void addOrdering(String sortPathSpec, SortOrder order) {
- query.addOrdering(sortPathSpec, order);
- }
-
- @Override
- public void addOrderings(Collection<? extends Ordering> orderings) {
- query.addOrderings(orderings);
- }
-
- @Override
- public PrefetchTreeNode addPrefetch(String prefetchPath) {
- return query.addPrefetch(prefetchPath);
- }
-
- @Override
- public void andQualifier(Expression e) {
- query.andQualifier(e);
- }
-
- @Override
- public void clearOrderings() {
- query.clearOrderings();
- }
-
- @Override
- public void clearPrefetches() {
- query.clearPrefetches();
- }
-
- @Override
- public SelectQuery<T> createQuery(Map<String, ?> parameters) {
- return query.createQuery(parameters);
- }
-
- @Override
- public SQLAction createSQLAction(SQLActionVisitor visitor) {
- return query.createSQLAction(visitor);
- }
-
- @Override
- public boolean equals(Object obj) {
- return query.equals(obj);
- }
-
- /**
- * @since 4.0
- */
- @Override
- public String getCacheGroup() {
- return super.getCacheGroup();
- }
-
- @Override
- public int getFetchLimit() {
- return query.getFetchLimit();
- }
-
- @Override
- public List<Ordering> getOrderings() {
- return query.getOrderings();
- }
-
- @Override
- public int getPageSize() {
- return query.getPageSize();
- }
-
- @Override
- public PrefetchTreeNode getPrefetchTree() {
- return query.getPrefetchTree();
- }
-
- @Override
- public Expression getQualifier() {
- return query.getQualifier();
- }
-
- @Override
- public Object getRoot() {
- return query.getRoot();
- }
-
- @Override
- public int hashCode() {
- return query.hashCode();
- }
-
- @Override
- public void initWithProperties(Map<String, ?> properties) {
- query.initWithProperties(properties);
- }
-
- @Override
- public boolean isDistinct() {
- return query.isDistinct();
- }
-
- @Override
- public boolean isFetchingDataRows() {
- return query.isFetchingDataRows();
- }
-
- @Override
- public void orQualifier(Expression e) {
- query.orQualifier(e);
- }
-
- @Override
- public SelectQuery<T> queryWithParameters(Map<String, ?> parameters, boolean pruneMissing) {
- return query.queryWithParameters(parameters, pruneMissing);
- }
-
- @Override
- public SelectQuery<T> queryWithParameters(Map<String, ?> parameters) {
- return query.queryWithParameters(parameters);
- }
-
- @Override
- public void removeOrdering(Ordering ordering) {
- query.removeOrdering(ordering);
- }
-
- @Override
- public void removePrefetch(String prefetchPath) {
- query.removePrefetch(prefetchPath);
- }
-
- @Override
- public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
- query.route(router, resolver, substitutedQuery);
- }
-
- /**
- * @since 4.0
- */
- @Override
- public void setCacheGroup(String cacheGroup) {
- query.setCacheGroup(cacheGroup);
- }
-
- @Override
- public void setDistinct(boolean distinct) {
- query.setDistinct(distinct);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void setFetchingDataRows(boolean flag) {
- query.setFetchingDataRows(flag);
- }
-
- @Override
- public void setFetchLimit(int fetchLimit) {
- query.setFetchLimit(fetchLimit);
- }
-
- @Override
- public void setPageSize(int pageSize) {
- query.setPageSize(pageSize);
- }
-
- @Override
- public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
- query.setPrefetchTree(prefetchTree);
- }
-
- @Override
- public void setQualifier(Expression qualifier) {
- query.setQualifier(qualifier);
- }
-
- @Override
- public void setRoot(Object value) {
- query.setRoot(value);
- }
-
- @Override
- public String toString() {
- return query.toString();
- }
-
- @Override
- public List<T> select(ObjectContext context) {
- return query.select(context);
- }
-
- @Override
- public T selectOne(ObjectContext context) {
- return query.selectOne(context);
- }
-
- @Override
- public T selectFirst(ObjectContext context) {
- return query.selectFirst(context);
- }
-
- @Override
- public void iterate(ObjectContext context, ResultIteratorCallback<T> callback) {
- query.iterate(context, callback);
- }
-
- @Override
- public ResultIterator<T> iterator(ObjectContext context) {
- return query.iterator(context);
- }
-
- @Override
- public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
- return query.batchIterator(context, size);
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
deleted file mode 100644
index 3c8b4d5..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
+++ /dev/null
@@ -1,55 +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.remote;
-
-import org.apache.cayenne.query.Query;
-
-/**
- * A message passed to a DataChannel to request a query execution with result returned as
- * QueryResponse.
- *
- * @since 1.2
- */
-public class QueryMessage implements ClientMessage {
-
- protected Query query;
-
- // for hessian serialization
- @SuppressWarnings("unused")
- private QueryMessage() {
-
- }
-
- public QueryMessage(Query query) {
- this.query = query;
- }
-
- public Query getQuery() {
- return query;
- }
-
- /**
- * Returns a description of the type of message. In this case always "Query".
- */
- @Override
- public String toString() {
- return "Query";
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
deleted file mode 100644
index f2b5e78..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
+++ /dev/null
@@ -1,156 +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.remote;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryCacheStrategy;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.QueryMetadataProxy;
-import org.apache.cayenne.query.QueryRouter;
-import org.apache.cayenne.query.SQLAction;
-import org.apache.cayenne.query.SQLActionVisitor;
-import org.apache.cayenne.reflect.ClassDescriptor;
-
-/**
- * A Query that fetches a range of objects from a previously fetched server-side paginated
- * list. This query is client-only and can't be executed on the server.
- *
- * @since 1.2
- */
-class RangeQuery implements Query {
-
- private String cacheKey;
- private int fetchOffset;
- private int fetchLimit;
- private Query originatingQuery;
-
- // exists for hessian serialization.
- @SuppressWarnings("unused")
- private RangeQuery() {
-
- }
-
- /**
- * Creates a query that returns a single page from an existing cached server-side
- * result list.
- */
- RangeQuery(String cacheKey, int fetchStartIndex, int fetchLimit,
- Query originatingQuery) {
- this.cacheKey = cacheKey;
- this.fetchOffset = fetchStartIndex;
- this.fetchLimit = fetchLimit;
- this.originatingQuery = originatingQuery;
- }
-
- public QueryMetadata getMetaData(EntityResolver resolver) {
- final QueryMetadata originatingMetadata = originatingQuery.getMetaData(resolver);
-
- return new QueryMetadataProxy(originatingMetadata) {
-
- public Query getOriginatingQuery() {
- return originatingQuery;
- }
-
- public List<Object> getResultSetMapping() {
- return null;
- }
-
- public boolean isSingleResultSetMapping() {
- return false;
- }
-
- public String getCacheKey() {
- return cacheKey;
- }
-
- public String getCacheGroup() {
- return null;
- }
-
- public int getFetchOffset() {
- return fetchOffset;
- }
-
- public int getFetchLimit() {
- return fetchLimit;
- }
-
- public int getPageSize() {
- return 0;
- }
-
- /**
- * @since 3.0
- */
- public QueryCacheStrategy getCacheStrategy() {
- return QueryCacheStrategy.getDefaultStrategy();
- }
-
- public DataMap getDataMap() {
- throw new UnsupportedOperationException();
- }
-
- public DbEntity getDbEntity() {
- throw new UnsupportedOperationException();
- }
-
- public ObjEntity getObjEntity() {
- throw new UnsupportedOperationException();
- }
-
- public ClassDescriptor getClassDescriptor() {
- throw new UnsupportedOperationException();
- }
-
- public Procedure getProcedure() {
- throw new UnsupportedOperationException();
- }
-
- public Map<String, String> getPathSplitAliases() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isRefreshingObjects() {
- throw new UnsupportedOperationException();
- }
-
- public int getStatementFetchSize() {
- return 0;
- }
- };
- }
-
- public SQLAction createSQLAction(SQLActionVisitor visitor) {
- throw new UnsupportedOperationException();
- }
-
- public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
- throw new UnsupportedOperationException();
- }
-
-}
[5/5] cayenne git commit: CAY-2373 cayenne-rop-server module - move
org.apache.cayenne.remote package to cayenne-rop server module - remove
dependencies from cayenne-server pom.xml - update tutorial
Posted by nt...@apache.org.
CAY-2373 cayenne-rop-server module
- move org.apache.cayenne.remote package to cayenne-rop server module
- remove dependencies from cayenne-server pom.xml
- update tutorial
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/38f37d79
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/38f37d79
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/38f37d79
Branch: refs/heads/master
Commit: 38f37d79aa6539913b83235fd4fff4ed6f826b85
Parents: eec08b7
Author: Nikita Timofeev <st...@gmail.com>
Authored: Tue Dec 12 15:52:43 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Tue Dec 12 15:52:43 2017 +0300
----------------------------------------------------------------------
cayenne-rop-server/pom.xml | 2 +
.../java/org/apache/cayenne/CayenneContext.java | 391 +++++++++++
.../cayenne/CayenneContextChildDiffLoader.java | 142 ++++
.../cayenne/CayenneContextGraphManager.java | 379 +++++++++++
.../cayenne/CayenneContextMergeHandler.java | 274 ++++++++
.../cayenne/CayenneContextQueryAction.java | 186 ++++++
.../apache/cayenne/remote/BootstrapMessage.java | 36 +
.../apache/cayenne/remote/ClientMessage.java | 31 +
.../apache/cayenne/remote/IncrementalQuery.java | 73 ++
.../cayenne/remote/IncrementalSelectQuery.java | 300 +++++++++
.../org/apache/cayenne/remote/QueryMessage.java | 55 ++
.../org/apache/cayenne/remote/RangeQuery.java | 156 +++++
.../remote/RemoteIncrementalFaultList.java | 668 +++++++++++++++++++
.../apache/cayenne/remote/RemoteService.java | 54 ++
.../apache/cayenne/remote/RemoteSession.java | 145 ++++
.../org/apache/cayenne/remote/SyncMessage.java | 91 +++
.../hessian/CayenneSerializerFactory.java | 43 ++
.../cayenne/remote/hessian/HessianConfig.java | 114 ++++
.../remote/hessian/service/HessianService.java | 64 ++
.../service/ServerDataRowSerializer.java | 56 ++
.../ServerPersistentObjectListSerializer.java | 50 ++
.../service/ServerSerializerFactory.java | 71 ++
.../remote/service/BaseRemoteService.java | 199 ++++++
.../cayenne/remote/service/DispatchHelper.java | 49 ++
.../remote/service/HttpRemoteService.java | 134 ++++
.../remote/service/MissingSessionException.java | 38 ++
.../cayenne/remote/service/ServerSession.java | 49 ++
.../cayenne/CayenneContextGraphManagerTest.java | 67 ++
.../cayenne/remote/MockRemoteService.java | 40 ++
.../cayenne/remote/RemoteSessionTest.java | 58 ++
.../remote/hessian/HessianConfigTest.java | 61 ++
.../hessian/MockAbstractSerializerFactory.java | 51 ++
.../hessian/service/HessianServiceTest.java | 73 ++
.../remote/service/BaseRemoteServiceTest.java | 142 ++++
.../remote/service/DispatchHelperTest.java | 54 ++
.../service/MockUnserializableException.java | 24 +
cayenne-server/pom.xml | 31 -
.../java/org/apache/cayenne/CayenneContext.java | 391 -----------
.../cayenne/CayenneContextChildDiffLoader.java | 142 ----
.../cayenne/CayenneContextGraphManager.java | 379 -----------
.../cayenne/CayenneContextMergeHandler.java | 274 --------
.../cayenne/CayenneContextQueryAction.java | 186 ------
.../apache/cayenne/remote/BootstrapMessage.java | 36 -
.../apache/cayenne/remote/ClientMessage.java | 31 -
.../apache/cayenne/remote/IncrementalQuery.java | 73 --
.../cayenne/remote/IncrementalSelectQuery.java | 300 ---------
.../org/apache/cayenne/remote/QueryMessage.java | 55 --
.../org/apache/cayenne/remote/RangeQuery.java | 156 -----
.../remote/RemoteIncrementalFaultList.java | 668 -------------------
.../apache/cayenne/remote/RemoteService.java | 54 --
.../apache/cayenne/remote/RemoteSession.java | 145 ----
.../org/apache/cayenne/remote/SyncMessage.java | 91 ---
.../hessian/CayenneSerializerFactory.java | 43 --
.../cayenne/remote/hessian/HessianConfig.java | 114 ----
.../remote/hessian/service/HessianService.java | 64 --
.../service/ServerDataRowSerializer.java | 56 --
.../ServerPersistentObjectListSerializer.java | 50 --
.../service/ServerSerializerFactory.java | 71 --
.../remote/service/BaseRemoteService.java | 199 ------
.../cayenne/remote/service/DispatchHelper.java | 49 --
.../remote/service/HttpRemoteService.java | 134 ----
.../remote/service/MissingSessionException.java | 38 --
.../cayenne/remote/service/ServerSession.java | 49 --
.../cayenne/CayenneContextGraphManagerTest.java | 67 --
.../cayenne/remote/MockRemoteService.java | 40 --
.../cayenne/remote/RemoteSessionTest.java | 58 --
.../remote/hessian/HessianConfigTest.java | 61 --
.../hessian/MockAbstractSerializerFactory.java | 51 --
.../hessian/service/HessianServiceTest.java | 73 --
.../remote/service/BaseRemoteServiceTest.java | 142 ----
.../remote/service/DispatchHelperTest.java | 54 --
.../service/MockUnserializableException.java | 24 -
.../tutorial/persistent/client/Main.java | 2 +-
tutorials/tutorial-rop-server/pom.xml | 3 +-
.../src/main/resources/cayenne-project.xml | 9 +-
.../src/main/resources/datamap.map.xml | 10 +-
76 files changed, 4433 insertions(+), 4460 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/pom.xml b/cayenne-rop-server/pom.xml
index eddbef1..93399a0 100644
--- a/cayenne-rop-server/pom.xml
+++ b/cayenne-rop-server/pom.xml
@@ -28,6 +28,8 @@
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.cayenne</groupId>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContext.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContext.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContext.java
new file mode 100644
index 0000000..c352ae0
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContext.java
@@ -0,0 +1,391 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.event.EventManager;
+import org.apache.cayenne.graph.CompoundDiff;
+import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.graph.GraphManager;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.util.EventUtil;
+import org.apache.cayenne.validation.ValidationException;
+import org.apache.cayenne.validation.ValidationResult;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A default generic implementation of ObjectContext suitable for accessing
+ * Cayenne from either an ORM or a client tiers. Communicates with Cayenne via a
+ * {@link org.apache.cayenne.DataChannel}.
+ *
+ * @since 1.2
+ */
+public class CayenneContext extends BaseContext {
+
+ CayenneContextGraphManager graphManager;
+
+ // object that merges "backdoor" changes that come from the channel.
+ CayenneContextMergeHandler mergeHandler;
+
+ /**
+ * Creates a new CayenneContext with no channel and disabled graph events.
+ */
+ public CayenneContext() {
+ this(null);
+ }
+
+ /**
+ * Creates a new CayenneContext, initializing it with a channel instance.
+ * CayenneContext created using this constructor WILL NOT broadcast graph
+ * change events.
+ */
+ public CayenneContext(DataChannel channel) {
+ this(channel, false, false);
+ }
+
+ /**
+ * Creates a new CayenneContext, initializing it with a channel.
+ */
+ public CayenneContext(DataChannel channel, boolean changeEventsEnabled, boolean lifecyleEventsEnabled) {
+
+ graphManager = new CayenneContextGraphManager(this, changeEventsEnabled, lifecyleEventsEnabled);
+
+ if (channel != null) {
+ attachToChannel(channel);
+ }
+ }
+
+ /**
+ * @since 3.1
+ */
+ @Override
+ protected void attachToChannel(DataChannel channel) {
+ super.attachToChannel(channel);
+
+ if (mergeHandler != null) {
+ mergeHandler.active = false;
+ mergeHandler = null;
+ }
+
+ EventManager eventManager = channel.getEventManager();
+ if (eventManager != null) {
+ mergeHandler = new CayenneContextMergeHandler(this);
+
+ // listen to our channel events...
+ // note that we must reset listener on channel switch, as there is
+ // no
+ // guarantee that a new channel uses the same EventManager.
+ EventUtil.listenForChannelEvents(channel, mergeHandler);
+ }
+ }
+
+ /**
+ * Returns true if this context posts individual object modification events.
+ * Subject used for these events is
+ * <code>ObjectContext.GRAPH_CHANGED_SUBJECT</code>.
+ */
+ public boolean isChangeEventsEnabled() {
+ return graphManager.changeEventsEnabled;
+ }
+
+ /**
+ * Returns true if this context posts lifecycle events. Subjects used for
+ * these events are
+ * <code>ObjectContext.GRAPH_COMMIT_STARTED_SUBJECT, ObjectContext.GRAPH_COMMITTED_SUBJECT,
+ * ObjectContext.GRAPH_COMMIT_ABORTED_SUBJECT, ObjectContext.GRAPH_ROLLEDBACK_SUBJECT.</code>
+ * .
+ */
+ public boolean isLifecycleEventsEnabled() {
+ return graphManager.lifecycleEventsEnabled;
+ }
+
+ @Override
+ public GraphManager getGraphManager() {
+ return graphManager;
+ }
+
+ CayenneContextGraphManager internalGraphManager() {
+ return graphManager;
+ }
+
+ /**
+ * Commits changes to uncommitted objects. First checks if there are changes
+ * in this context and if any changes are detected, sends a commit message
+ * to remote Cayenne service via an internal instance of CayenneConnector.
+ */
+ @Override
+ public void commitChanges() {
+ doCommitChanges(true);
+ }
+
+ GraphDiff doCommitChanges(boolean cascade) {
+
+ int syncType = cascade ? DataChannel.FLUSH_CASCADE_SYNC : DataChannel.FLUSH_NOCASCADE_SYNC;
+
+ GraphDiff commitDiff = null;
+
+ synchronized (graphManager) {
+
+ if (graphManager.hasChanges()) {
+
+ if (isValidatingObjectsOnCommit()) {
+ ValidationResult result = new ValidationResult();
+ Iterator<?> it = graphManager.dirtyNodes().iterator();
+ while (it.hasNext()) {
+ Persistent p = (Persistent) it.next();
+ if (p instanceof Validating) {
+ switch (p.getPersistenceState()) {
+ case PersistenceState.NEW:
+ ((Validating) p).validateForInsert(result);
+ break;
+ case PersistenceState.MODIFIED:
+ ((Validating) p).validateForUpdate(result);
+ break;
+ case PersistenceState.DELETED:
+ ((Validating) p).validateForDelete(result);
+ break;
+ }
+ }
+ }
+
+ if (result.hasFailures()) {
+ throw new ValidationException(result);
+ }
+ }
+
+ graphManager.graphCommitStarted();
+
+ GraphDiff changes = graphManager.getDiffsSinceLastFlush();
+
+ try {
+ commitDiff = channel.onSync(this, changes, syncType);
+ } catch (Throwable th) {
+ graphManager.graphCommitAborted();
+
+ if (th instanceof CayenneRuntimeException) {
+ throw (CayenneRuntimeException) th;
+ } else {
+ throw new CayenneRuntimeException("Commit error", th);
+ }
+ }
+
+ graphManager.graphCommitted(commitDiff);
+
+ // this event is caught by peer nested ObjectContexts to
+ // synchronize the
+ // state
+ fireDataChannelCommitted(this, changes);
+ }
+ }
+
+ return commitDiff;
+ }
+
+ @Override
+ public void commitChangesToParent() {
+ doCommitChanges(false);
+ }
+
+ @Override
+ public void rollbackChanges() {
+ synchronized (graphManager) {
+ if (graphManager.hasChanges()) {
+
+ GraphDiff diff = graphManager.getDiffs();
+ graphManager.graphReverted();
+
+ channel.onSync(this, diff, DataChannel.ROLLBACK_CASCADE_SYNC);
+ fireDataChannelRolledback(this, diff);
+ }
+ }
+ }
+
+ @Override
+ public void rollbackChangesLocally() {
+ synchronized (graphManager) {
+ if (graphManager.hasChanges()) {
+ GraphDiff diff = graphManager.getDiffs();
+ graphManager.graphReverted();
+
+ fireDataChannelRolledback(this, diff);
+ }
+ }
+ }
+
+ /**
+ * Creates and registers a new Persistent object instance.
+ */
+ @Override
+ public <T> T newObject(Class<T> persistentClass) {
+ if (persistentClass == null) {
+ throw new NullPointerException("Persistent class can't be null.");
+ }
+
+ ObjEntity entity = getEntityResolver().getObjEntity(persistentClass);
+ if (entity == null) {
+ throw new CayenneRuntimeException("No entity mapped for class: %s", persistentClass);
+ }
+
+ ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
+ @SuppressWarnings("unchecked")
+ T object = (T) descriptor.createObject();
+ descriptor.injectValueHolders(object);
+ registerNewObject((Persistent) object, entity.getName(), descriptor);
+ return object;
+ }
+
+ /**
+ * @since 3.0
+ */
+ @Override
+ public void registerNewObject(Object object) {
+ if (object == null) {
+ throw new NullPointerException("An attempt to register null object.");
+ }
+
+ ObjEntity entity = getEntityResolver().getObjEntity(object.getClass());
+ ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
+ registerNewObject((Persistent) object, entity.getName(), descriptor);
+ }
+
+ /**
+ * Runs a query, returning result as list.
+ */
+ @Override
+ public List performQuery(Query query) {
+ List result = onQuery(this, query).firstList();
+ return result != null ? result : new ArrayList<>(1);
+ }
+
+ @Override
+ public QueryResponse performGenericQuery(Query query) {
+ return onQuery(this, query);
+ }
+
+ public QueryResponse onQuery(ObjectContext context, Query query) {
+ return new CayenneContextQueryAction(this, context, query).execute();
+ }
+
+ @Override
+ public Collection<?> uncommittedObjects() {
+ synchronized (graphManager) {
+ return graphManager.dirtyNodes();
+ }
+ }
+
+ @Override
+ public Collection<?> deletedObjects() {
+ synchronized (graphManager) {
+ return graphManager.dirtyNodes(PersistenceState.DELETED);
+ }
+ }
+
+ @Override
+ public Collection<?> modifiedObjects() {
+ synchronized (graphManager) {
+ return graphManager.dirtyNodes(PersistenceState.MODIFIED);
+ }
+ }
+
+ @Override
+ public Collection<?> newObjects() {
+ synchronized (graphManager) {
+ return graphManager.dirtyNodes(PersistenceState.NEW);
+ }
+ }
+
+ // ****** non-public methods ******
+
+ void registerNewObject(Persistent object, String entityName, ClassDescriptor descriptor) {
+ /**
+ * We should create new id only if it is not set for this object. It
+ * could have been created, for instance, in child context
+ */
+ ObjectId id = object.getObjectId();
+ if (id == null) {
+ id = new ObjectId(entityName);
+ object.setObjectId(id);
+ }
+
+ injectInitialValue(object);
+ }
+
+ Persistent createFault(ObjectId id) {
+ ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
+
+ Persistent object = (Persistent) descriptor.createObject();
+
+ object.setPersistenceState(PersistenceState.HOLLOW);
+ object.setObjectContext(this);
+ object.setObjectId(id);
+
+ graphManager.registerNode(id, object);
+
+ return object;
+ }
+
+ @Override
+ protected GraphDiff onContextFlush(ObjectContext originatingContext, GraphDiff changes, boolean cascade) {
+
+ boolean childContext = this != originatingContext && changes != null;
+
+ if (childContext) {
+
+ // PropertyChangeProcessingStrategy oldStrategy =
+ // getPropertyChangeProcessingStrategy();
+ // setPropertyChangeProcessingStrategy(PropertyChangeProcessingStrategy.RECORD);
+ try {
+ changes.apply(new CayenneContextChildDiffLoader(this));
+ } finally {
+ // setPropertyChangeProcessingStrategy(oldStrategy);
+ }
+
+ fireDataChannelChanged(originatingContext, changes);
+ }
+
+ return (cascade) ? doCommitChanges(true) : new CompoundDiff();
+ }
+
+ /**
+ * Returns <code>true</code> if there are any modified, deleted or new
+ * objects registered with this CayenneContext, <code>false</code>
+ * otherwise.
+ */
+ public boolean hasChanges() {
+ return graphManager.hasChanges();
+ }
+
+ /**
+ * This method simply returns an iterator over a list of selected objects.
+ * There's no performance benefit of using it vs. regular "select".
+ *
+ * @since 4.0
+ */
+ public <T> ResultIterator<T> iterator(org.apache.cayenne.query.Select<T> query) {
+ List<T> objects = select(query);
+ return new CollectionResultIterator<T>(objects);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
new file mode 100644
index 0000000..151b72e
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
@@ -0,0 +1,142 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.graph.ChildDiffLoader;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+
+/**
+ * Used for loading child's CayenneContext changes to parent context.
+ *
+ * @since 3.0
+ */
+class CayenneContextChildDiffLoader extends ChildDiffLoader {
+
+ public CayenneContextChildDiffLoader(CayenneContext context) {
+ super(context);
+ }
+
+ @Override
+ public void nodePropertyChanged(
+ Object nodeId,
+ String property,
+ Object oldValue,
+ Object newValue) {
+
+ super.nodePropertyChanged(nodeId, property, oldValue, newValue);
+
+ Persistent object = (Persistent) context.getGraphManager().getNode(nodeId);
+ context.propertyChanged(object, property, oldValue, newValue);
+ }
+
+ @Override
+ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+
+ final Persistent source = findObject(nodeId);
+ final Persistent target = findObject(targetNodeId);
+
+ // if a target was later deleted, the diff for arcCreated is still preserved and
+ // can result in NULL target here.
+ if (target == null) {
+ return;
+ }
+
+ ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
+ ((ObjectId) nodeId).getEntityName());
+ ArcProperty property = (ArcProperty) descriptor.getProperty(arcId.toString());
+
+ property.visit(new PropertyVisitor() {
+
+ public boolean visitAttribute(AttributeProperty property) {
+ return false;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ property.addTargetDirectly(source, target);
+ return false;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ property.setTarget(source, target, false);
+ return false;
+ }
+ });
+ context.propertyChanged(source, (String) arcId, null, target);
+ }
+
+ @Override
+ public void arcDeleted(Object nodeId, final Object targetNodeId, Object arcId) {
+ final Persistent source = findObject(nodeId);
+
+ // needed as sometime temporary objects are evoked from the context before
+ // changing their relationships
+ if (source == null) {
+ return;
+ }
+
+ ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
+ ((ObjectId) nodeId).getEntityName());
+ PropertyDescriptor property = descriptor.getProperty(arcId.toString());
+
+ final Persistent[] target = new Persistent[1];
+ target[0] = findObject(targetNodeId);
+
+ property.visit(new PropertyVisitor() {
+
+ public boolean visitAttribute(AttributeProperty property) {
+ return false;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ if (target[0] == null) {
+
+ // this is usually the case when a NEW object was deleted and then
+ // its relationships were manipulated; so try to locate the object
+ // in the collection ... the performance of this is rather dubious
+ // of course...
+ target[0] = findObjectInCollection(targetNodeId, property
+ .readProperty(source));
+ }
+
+ if (target[0] == null) {
+ // ignore?
+ }
+ else {
+ property.removeTargetDirectly(source, target[0]);
+ }
+
+ return false;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ property.setTarget(source, null, false);
+ return false;
+ }
+ });
+
+ context.propertyChanged(source, (String) arcId, target[0], null);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
new file mode 100644
index 0000000..f4865c0
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
@@ -0,0 +1,379 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.event.EventManager;
+import org.apache.cayenne.event.EventSubject;
+import org.apache.cayenne.graph.ArcCreateOperation;
+import org.apache.cayenne.graph.ArcDeleteOperation;
+import org.apache.cayenne.graph.GraphChangeHandler;
+import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.graph.GraphEvent;
+import org.apache.cayenne.graph.GraphMap;
+import org.apache.cayenne.graph.NodeCreateOperation;
+import org.apache.cayenne.graph.NodeDeleteOperation;
+import org.apache.cayenne.graph.NodeIdChangeOperation;
+import org.apache.cayenne.graph.NodePropertyChangeOperation;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyException;
+import org.apache.cayenne.reflect.ToManyMapProperty;
+import org.apache.cayenne.util.PersistentObjectMap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A GraphMap extension that works together with {@link ObjectContext} to track persistent object
+ * changes and send events.
+ *
+ * @since 1.2
+ */
+final class CayenneContextGraphManager extends GraphMap {
+
+ static final String COMMIT_MARKER = "commit";
+ static final String FLUSH_MARKER = "flush";
+
+ CayenneContext context;
+ Collection<Object> deadIds;
+ boolean changeEventsEnabled;
+ boolean lifecycleEventsEnabled;
+
+ ObjectContextStateLog stateLog;
+ ObjectContextChangeLog changeLog;
+
+ CayenneContextGraphManager(CayenneContext context, boolean changeEventsEnabled,
+ boolean lifecycleEventsEnabled) {
+
+ this.context = context;
+ this.changeEventsEnabled = changeEventsEnabled;
+ this.lifecycleEventsEnabled = lifecycleEventsEnabled;
+
+ this.stateLog = new ObjectContextStateLog(this);
+ this.changeLog = new ObjectContextChangeLog();
+ }
+
+ boolean hasChanges() {
+ return changeLog.size() > 0;
+ }
+
+ boolean hasChangesSinceLastFlush() {
+ int size = changeLog.hasMarker(FLUSH_MARKER) ? changeLog
+ .sizeAfterMarker(FLUSH_MARKER) : changeLog.size();
+ return size > 0;
+ }
+
+ GraphDiff getDiffs() {
+ return changeLog.getDiffs();
+ }
+
+ GraphDiff getDiffsSinceLastFlush() {
+ return changeLog.hasMarker(FLUSH_MARKER) ? changeLog
+ .getDiffsAfterMarker(FLUSH_MARKER) : changeLog.getDiffs();
+ }
+
+ Collection<Object> dirtyNodes() {
+ return stateLog.dirtyNodes();
+ }
+
+ Collection<Object> dirtyNodes(int state) {
+ return stateLog.dirtyNodes(state);
+ }
+
+ @Override
+ public synchronized Object unregisterNode(Object nodeId) {
+ Object node = super.unregisterNode(nodeId);
+
+ // remove node from other collections...
+ if (node != null) {
+ stateLog.unregisterNode(nodeId);
+ changeLog.unregisterNode(nodeId);
+ Persistent object = (Persistent)node;
+ object.setObjectContext(null);
+ object.setPersistenceState(PersistenceState.TRANSIENT);
+ return node;
+ }
+
+ return null;
+ }
+
+ // ****** Sync Events API *****
+ /**
+ * Clears commit marker, but keeps all recorded operations.
+ */
+ void graphCommitAborted() {
+ changeLog.removeMarker(COMMIT_MARKER);
+ }
+
+ /**
+ * Sets commit start marker in the change log. If events are enabled, posts commit
+ * start event.
+ */
+ void graphCommitStarted() {
+ changeLog.setMarker(COMMIT_MARKER);
+ }
+
+ void graphCommitted(GraphDiff parentSyncDiff) {
+ if (parentSyncDiff != null) {
+ new CayenneContextMergeHandler(context).merge(parentSyncDiff);
+ }
+
+ remapTargets();
+
+ stateLog.graphCommitted();
+ reset();
+
+ if (lifecycleEventsEnabled) {
+ // include all diffs after the commit start marker.
+ // We fire event as if it was posted by parent channel, so that
+ // nested contexts could catch it
+ context.fireDataChannelCommitted(context.getChannel(), parentSyncDiff);
+ }
+ }
+
+ /**
+ * Remaps keys in to-many map relationships that contain dirty objects with
+ * potentially modified properties.
+ */
+ private void remapTargets() {
+
+ Iterator<Object> it = stateLog.dirtyIds().iterator();
+
+ EntityResolver resolver = context.getEntityResolver();
+
+ while (it.hasNext()) {
+ ObjectId id = (ObjectId) it.next();
+ ClassDescriptor descriptor = resolver.getClassDescriptor(id.getEntityName());
+
+ Collection<ArcProperty> mapArcProperties = descriptor.getMapArcProperties();
+ if (!mapArcProperties.isEmpty()) {
+
+ Object object = getNode(id);
+
+ for (ArcProperty arc : mapArcProperties) {
+ ToManyMapProperty reverseArc = (ToManyMapProperty) arc
+ .getComplimentaryReverseArc();
+
+ Object source = arc.readPropertyDirectly(object);
+ if (source != null && !reverseArc.isFault(source)) {
+ remapTarget(reverseArc, source, object);
+ }
+ }
+ }
+ }
+ }
+
+ // clone of DataDomainSyncBucket.remapTarget
+ private final void remapTarget(
+ ToManyMapProperty property,
+ Object source,
+ Object target) throws PropertyException {
+
+ @SuppressWarnings("unchecked")
+ Map<Object, Object> map = (Map<Object, Object>) property.readProperty(source);
+
+ Object newKey = property.getMapKey(target);
+ Object currentValue = map.get(newKey);
+
+ if (currentValue == target) {
+ // nothing to do
+ return;
+ }
+ // else - do not check for conflicts here (i.e. another object mapped for the same
+ // key), as we have no control of the order in which this method is called, so
+ // another object may be remapped later by the caller
+
+ // must do a slow map scan to ensure the object is not mapped under a different
+ // key...
+ Iterator<?> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<?, ?> e = (Map.Entry<?, ?>) it.next();
+ if (e.getValue() == target) {
+ // this remove does not trigger event in PersistentObjectMap
+ it.remove();
+ break;
+ }
+ }
+
+ // TODO: (andrey, 25/11/09 - this is a hack to prevent event triggering
+ // (and concurrent exceptions)
+ // should find a way to get rid of type casting
+ ((PersistentObjectMap) map).putDirectly(newKey, target);
+ }
+
+ void graphFlushed() {
+ changeLog.setMarker(FLUSH_MARKER);
+ }
+
+ void graphReverted() {
+ GraphDiff diff = changeLog.getDiffs();
+
+ diff.undo(new RollbackChangeHandler());
+ stateLog.graphReverted();
+ reset();
+
+ if (lifecycleEventsEnabled) {
+ context.fireDataChannelRolledback(context, diff);
+ }
+ }
+
+ // ****** GraphChangeHandler API ******
+ // =====================================================
+
+ @Override
+ public synchronized void nodeIdChanged(Object nodeId, Object newId) {
+ stateLog.nodeIdChanged(nodeId, newId);
+ processChange(new NodeIdChangeOperation(nodeId, newId));
+ }
+
+ @Override
+ public synchronized void nodeCreated(Object nodeId) {
+ stateLog.nodeCreated(nodeId);
+ processChange(new NodeCreateOperation(nodeId));
+ }
+
+ @Override
+ public synchronized void nodeRemoved(Object nodeId) {
+ stateLog.nodeRemoved(nodeId);
+ processChange(new NodeDeleteOperation(nodeId));
+ }
+
+ @Override
+ public synchronized void nodePropertyChanged(
+ Object nodeId,
+ String property,
+ Object oldValue,
+ Object newValue) {
+
+ stateLog.nodePropertyChanged(nodeId, property, oldValue, newValue);
+ processChange(new NodePropertyChangeOperation(
+ nodeId,
+ property,
+ oldValue,
+ newValue));
+ }
+
+ @Override
+ public synchronized void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+ stateLog.arcCreated(nodeId, targetNodeId, arcId);
+ processChange(new ArcCreateOperation(nodeId, targetNodeId, arcId));
+ }
+
+ @Override
+ public synchronized void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+ stateLog.arcDeleted(nodeId, targetNodeId, arcId);
+ processChange(new ArcDeleteOperation(nodeId, targetNodeId, arcId));
+ }
+
+ // ****** helper methods ******
+ // =====================================================
+
+ private void processChange(GraphDiff diff) {
+ changeLog.addOperation(diff);
+
+ if (changeEventsEnabled) {
+ context.fireDataChannelChanged(context, diff);
+ }
+ }
+
+ /**
+ * Wraps GraphDiff in a GraphEvent and sends it via EventManager with specified
+ * subject.
+ */
+ void send(GraphDiff diff, EventSubject subject, Object eventSource) {
+ EventManager manager = (context.getChannel() != null) ? context
+ .getChannel()
+ .getEventManager() : null;
+
+ if (manager != null) {
+ GraphEvent e = new GraphEvent(context, eventSource, diff);
+ manager.postEvent(e, subject);
+ }
+ }
+
+ void reset() {
+ changeLog.reset();
+
+ if (deadIds != null) {
+ // unregister dead ids...
+ for (final Object deadId : deadIds) {
+ nodes.remove(deadId);
+ }
+
+ deadIds = null;
+ }
+ }
+
+ Collection<Object> deadIds() {
+ if (deadIds == null) {
+ deadIds = new ArrayList<>();
+ }
+
+ return deadIds;
+ }
+
+ /**
+ * This change handler is used to perform rollback actions for Cayenne context
+ */
+ class RollbackChangeHandler implements GraphChangeHandler {
+
+ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+ context.mergeHandler.arcCreated(nodeId, targetNodeId, arcId);
+ CayenneContextGraphManager.this.arcCreated(nodeId, targetNodeId, arcId);
+ }
+
+ public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+ context.mergeHandler.arcDeleted(nodeId, targetNodeId, arcId);
+ CayenneContextGraphManager.this.arcDeleted(nodeId, targetNodeId, arcId);
+ }
+
+ public void nodeCreated(Object nodeId) {
+ CayenneContextGraphManager.this.nodeCreated(nodeId);
+ }
+
+ public void nodeIdChanged(Object nodeId, Object newId) {
+ CayenneContextGraphManager.this.nodeIdChanged(nodeId, newId);
+ }
+
+ /**
+ * Need to write property directly to this context
+ */
+ public void nodePropertyChanged(
+ Object nodeId,
+ String property,
+ Object oldValue,
+ Object newValue) {
+ context.mergeHandler
+ .nodePropertyChanged(nodeId, property, oldValue, newValue);
+ CayenneContextGraphManager.this.nodePropertyChanged(
+ nodeId,
+ property,
+ oldValue,
+ newValue);
+ }
+
+ public void nodeRemoved(Object nodeId) {
+ CayenneContextGraphManager.this.nodeRemoved(nodeId);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
new file mode 100644
index 0000000..f490128
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
@@ -0,0 +1,274 @@
+/*****************************************************************
+ * 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;
+
+import org.apache.cayenne.graph.GraphChangeHandler;
+import org.apache.cayenne.graph.GraphDiff;
+import org.apache.cayenne.graph.GraphEvent;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyDescriptor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.util.Util;
+
+/**
+ * An object that merges "backdoor" modifications of the object graph coming from the
+ * underlying DataChannel. When doing an update, CayenneContextMergeHandler blocks
+ * broadcasting of GraphManager events.
+ *
+ * @since 1.2
+ */
+class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListener {
+
+ CayenneContext context;
+ boolean active;
+
+ CayenneContextMergeHandler(CayenneContext context) {
+ this.context = context;
+ this.active = true;
+ }
+
+ // ******* DataChannelListener methods *******
+
+ public void graphChanged(final GraphEvent e) {
+ // process flush
+ if (shouldProcessEvent(e) && e.getDiff() != null) {
+ runWithEventsDisabled(new Runnable() {
+
+ public void run() {
+ e.getDiff().apply(CayenneContextMergeHandler.this);
+
+ }
+ });
+
+ // post event outside of "execute" to make sure it is sent
+ repostAfterMerge(e);
+ }
+ }
+
+ public void graphFlushed(final GraphEvent e) {
+ // TODO (Andrus, 10/17/2005) - there are a few problems with commit processing:
+
+ // 1. Event mechanism reliability:
+ // - events may come out of order (commit and then preceeding flush)
+ // - events may be missing all together (commit arrived, while prior flush did
+ // not)
+ // Possible solution - an "event_version_id" to be used for optimistic locking
+
+ // 2. We don't know if our own dirty objects were committed or not...
+ // For now we will simply merge the changes, and keep the context dirty
+
+ if (shouldProcessEvent(e)) {
+
+ runWithEventsDisabled(new Runnable() {
+
+ public void run() {
+
+ if (e.getDiff() != null) {
+ e.getDiff().apply(CayenneContextMergeHandler.this);
+ }
+ }
+ });
+
+ // post event outside of "execute" to make sure it is sent
+ repostAfterMerge(e);
+ }
+ }
+
+ public void graphRolledback(final GraphEvent e) {
+
+ // TODO: andrus, 3/29/2007: per CAY-771, if a LOCAL peer context posted the event,
+ // just ignore it, however if the REMOTE peer reverted the parent remote
+ // DataContext, we need to invalidate stale committed objects...
+ }
+
+ // ******* End DataChannelListener methods *******
+
+ void repostAfterMerge(GraphEvent originalEvent) {
+ // though the subject is CHANGE, "merge" events are really lifecycle.
+ if (context.isLifecycleEventsEnabled()) {
+ context.fireDataChannelChanged(originalEvent.getSource(), originalEvent.getDiff());
+ }
+ }
+
+ /**
+ * Executes merging of the external diff.
+ */
+ void merge(final GraphDiff diff) {
+ runWithEventsDisabled(new Runnable() {
+
+ public void run() {
+ diff.apply(CayenneContextMergeHandler.this);
+ }
+ });
+ }
+
+ // ******* GraphChangeHandler methods *********
+
+ public void nodeIdChanged(Object nodeId, Object newId) {
+ // do not unregister the node just yet... only put replaced id in deadIds to
+ // remove it later. Otherwise stored operations will not work
+ Object node = context.internalGraphManager().getNode(nodeId);
+
+ if (node != null) {
+ context.internalGraphManager().deadIds().add(nodeId);
+ context.internalGraphManager().registerNode(newId, node);
+
+ if (node instanceof Persistent) {
+ // inject new id
+ ((Persistent) node).setObjectId((ObjectId) newId);
+ }
+ }
+ }
+
+ public void nodeCreated(Object nodeId) {
+ // ignore
+ }
+
+ public void nodeRemoved(Object nodeId) {
+ context.getGraphManager().unregisterNode(nodeId);
+ }
+
+ public void nodePropertyChanged(
+ Object nodeId,
+ String property,
+ Object oldValue,
+ Object newValue) {
+
+ Object object = context.internalGraphManager().getNode(nodeId);
+ if (object != null) {
+
+ // do not override local changes....
+ PropertyDescriptor p = propertyForId(nodeId, property);
+ if (Util.nullSafeEquals(p.readPropertyDirectly(object), oldValue)) {
+
+ p.writePropertyDirectly(object, oldValue, newValue);
+ }
+ }
+ }
+
+ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+ // null source or target likely means the object is not faulted yet... Faults
+ // shouldn't get disturbed by adding/removing arcs
+
+ Object source = context.internalGraphManager().getNode(nodeId);
+ if (source == null) {
+ // no need to connect non-existent object
+ return;
+ }
+
+ // TODO (Andrus, 10/17/2005) - check for local modifications to avoid
+ // overwriting...
+
+ ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
+ if (p.isFault(source)) {
+ return;
+ }
+
+ Object target = context.internalGraphManager().getNode(targetNodeId);
+ if (target == null) {
+ target = context.createFault((ObjectId) targetNodeId);
+ }
+
+ try {
+ if (p instanceof ToManyProperty) {
+ ((ToManyProperty) p).addTargetDirectly(source, target);
+ }
+ else {
+ p.writePropertyDirectly(source, null, target);
+ }
+ }
+ finally {
+ }
+ }
+
+ public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+
+ // null source or target likely means the object is not faulted yet... Faults
+ // shouldn't get disturbed by adding/removing arcs
+
+ Object source = context.internalGraphManager().getNode(nodeId);
+ if (source == null) {
+ // no need to disconnect non-existent object
+ return;
+ }
+
+ // (see "TODO" in 'arcCreated')
+ ArcProperty p = (ArcProperty) propertyForId(nodeId, arcId.toString());
+ if (p.isFault(source)) {
+ return;
+ }
+
+ Object target = context.internalGraphManager().getNode(targetNodeId);
+ if (target == null) {
+ target = context.createFault((ObjectId) targetNodeId);
+ }
+
+ try {
+ if (p instanceof ToManyProperty) {
+ ((ToManyProperty) p).removeTargetDirectly(source, target);
+ }
+ else {
+ p.writePropertyDirectly(source, target, null);
+ }
+ }
+ finally {
+ }
+ }
+
+ private PropertyDescriptor propertyForId(Object nodeId, String propertyName) {
+ ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
+ ((ObjectId) nodeId).getEntityName());
+ return descriptor.getProperty(propertyName);
+ }
+
+ // Returns true if this object is active; an event came from our channel, but did not
+ // originate in it.
+ boolean shouldProcessEvent(GraphEvent e) {
+ // only process events that came from our channel, but did not originate in it
+ // (i.e. likely posted by EventBridge)
+ return active
+ && e.getSource() == context.getChannel()
+ && e.getPostedBy() != context
+ && e.getPostedBy() != context.getChannel();
+ }
+
+ // executes a closure, disabling ObjectContext events for the duration of the
+ // execution.
+
+ private void runWithEventsDisabled(Runnable closure) {
+
+ synchronized (context.internalGraphManager()) {
+ boolean changeEventsEnabled = context.internalGraphManager().changeEventsEnabled;
+ context.internalGraphManager().changeEventsEnabled = false;
+
+ boolean lifecycleEventsEnabled = context.internalGraphManager().lifecycleEventsEnabled;
+ context.internalGraphManager().lifecycleEventsEnabled = false;
+
+ try {
+ closure.run();
+ }
+ finally {
+ context.internalGraphManager().changeEventsEnabled = changeEventsEnabled;
+ context.internalGraphManager().lifecycleEventsEnabled = lifecycleEventsEnabled;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
new file mode 100644
index 0000000..e0b693c
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
@@ -0,0 +1,186 @@
+/*****************************************************************
+ * 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;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.cayenne.cache.QueryCacheEntryFactory;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.RefreshQuery;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.remote.RemoteIncrementalFaultList;
+import org.apache.cayenne.util.ListResponse;
+import org.apache.cayenne.util.ObjectContextQueryAction;
+
+/**
+ * @since 1.2
+ */
+class CayenneContextQueryAction extends ObjectContextQueryAction {
+
+ CayenneContextQueryAction(CayenneContext actingContext, ObjectContext targetContext,
+ Query query) {
+ super(actingContext, targetContext, query);
+ }
+
+ @Override
+ protected boolean interceptPaginatedQuery() {
+ if (metadata.getPageSize() > 0) {
+ response = new ListResponse(new RemoteIncrementalFaultList(
+ actingContext,
+ query));
+ return DONE;
+ }
+
+ return !DONE;
+ }
+
+ @Override
+ protected QueryCacheEntryFactory getCacheObjectFactory() {
+ return new QueryCacheEntryFactory() {
+
+ public List createObject() {
+ if (interceptPaginatedQuery() != DONE) {
+ runQuery();
+ }
+ return response.firstList();
+ }
+ };
+ }
+
+ @Override
+ protected boolean interceptRefreshQuery() {
+ if (query instanceof RefreshQuery) {
+ RefreshQuery refreshQuery = (RefreshQuery) query;
+
+ CayenneContext context = (CayenneContext) actingContext;
+
+ // handle 4 separate scenarios, but do not combine them as it will be
+ // unclear how to handle cascading behavior
+
+ // 1. refresh all
+ if (refreshQuery.isRefreshAll()) {
+
+ invalidateLocally(context.internalGraphManager(), context
+ .internalGraphManager()
+ .registeredNodes()
+ .iterator());
+ context.getQueryCache().clear();
+
+ // cascade
+ return !DONE;
+ }
+
+ // 2. invalidate object collection
+ Collection<?> objects = refreshQuery.getObjects();
+ if (objects != null && !objects.isEmpty()) {
+
+ invalidateLocally(context.internalGraphManager(), objects.iterator());
+
+ // cascade
+ return !DONE;
+ }
+
+ // 3. refresh query - have to do it eagerly to refresh the objects involved
+ if (refreshQuery.getQuery() != null) {
+ Query cachedQuery = refreshQuery.getQuery();
+
+ String cacheKey = cachedQuery
+ .getMetaData(context.getEntityResolver())
+ .getCacheKey();
+ context.getQueryCache().remove(cacheKey);
+
+ this.response = context.performGenericQuery(cachedQuery);
+
+ // do not cascade to avoid running query twice
+ return DONE;
+ }
+
+ // 4. refresh groups...
+ if (refreshQuery.getGroupKeys() != null
+ && refreshQuery.getGroupKeys().length > 0) {
+
+ String[] groups = refreshQuery.getGroupKeys();
+ for (String group : groups) {
+ context.getQueryCache().removeGroup(group);
+ }
+
+ // cascade group invalidation
+ return !DONE;
+ }
+ }
+
+ return !DONE;
+ }
+
+ private void invalidateLocally(CayenneContextGraphManager graphManager, Iterator<?> it) {
+ if (!it.hasNext()) {
+ return;
+ }
+
+ EntityResolver resolver = actingContext.getEntityResolver();
+
+ while (it.hasNext()) {
+ final Persistent object = (Persistent) it.next();
+
+ // we don't care about NEW objects,
+ // but we still do care about HOLLOW, since snapshot might still be
+ // present
+ if (object.getPersistenceState() == PersistenceState.NEW) {
+ continue;
+ }
+
+ ObjectId id = object.getObjectId();
+
+ // per CAY-1082 ROP objects (unlike CayenneDataObject) require all
+ // relationship faults invalidation.
+ ClassDescriptor descriptor = resolver.getClassDescriptor(id.getEntityName());
+ PropertyVisitor arcInvalidator = new PropertyVisitor() {
+
+ public boolean visitAttribute(AttributeProperty property) {
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ property.invalidate(object);
+ return true;
+ }
+
+ public boolean visitToOne(ToOneProperty property) {
+ property.invalidate(object);
+ return true;
+ }
+ };
+
+ descriptor.visitProperties(arcInvalidator);
+ object.setPersistenceState(PersistenceState.HOLLOW);
+
+ // remove cached changes
+ graphManager.changeLog.unregisterNode(id);
+ graphManager.stateLog.unregisterNode(id);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
new file mode 100644
index 0000000..26f2f1b
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/BootstrapMessage.java
@@ -0,0 +1,36 @@
+/*****************************************************************
+ * 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.remote;
+
+/**
+ * A message sent to a remote service to request Cayenne mapping info.
+ *
+ * @since 1.2
+ */
+public class BootstrapMessage implements ClientMessage {
+
+ /**
+ * Returns a description of the type of message. In this case always "Bootstrap".
+ */
+ @Override
+ public String toString() {
+ return "Bootstrap";
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
new file mode 100644
index 0000000..e0740b3
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/ClientMessage.java
@@ -0,0 +1,31 @@
+/*****************************************************************
+ * 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.remote;
+
+import java.io.Serializable;
+
+/**
+ * A tag interface representing a message sent by a remote client to Cayenne service.
+ *
+ * @since 1.2
+ */
+public interface ClientMessage extends Serializable {
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
new file mode 100644
index 0000000..51c7946
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalQuery.java
@@ -0,0 +1,73 @@
+/*****************************************************************
+ * 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.remote;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.QueryMetadataProxy;
+import org.apache.cayenne.query.QueryRouter;
+import org.apache.cayenne.query.SQLAction;
+import org.apache.cayenne.query.SQLActionVisitor;
+
+/**
+ * A client wrapper for the incremental query that overrides the metadata to ensure that
+ * query result is cached on the server, so that subranges could be retrieved at a later
+ * time.
+ *
+ * @since 1.2
+ */
+class IncrementalQuery implements Query {
+
+ private Query query;
+ private String cacheKey;
+
+ IncrementalQuery(Query query, String cacheKey) {
+ this.query = query;
+ this.cacheKey = cacheKey;
+ }
+
+ public QueryMetadata getMetaData(EntityResolver resolver) {
+ final QueryMetadata metadata = query.getMetaData(resolver);
+
+ // the way paginated queries work on the server is that they are never cached
+ // (IncrementalFaultList interception happens before cache interception). So
+ // overriding caching settings in the metadata will only affect
+ // ClientServerChannel behavior
+ return new QueryMetadataProxy(metadata) {
+ public Query getOriginatingQuery() {
+ return null;
+ }
+
+ public String getCacheKey() {
+ return cacheKey;
+ }
+ };
+ }
+
+ public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
+ query.route(router, resolver, substitutedQuery);
+ }
+
+ public SQLAction createSQLAction(SQLActionVisitor visitor) {
+ return query.createSQLAction(visitor);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
new file mode 100644
index 0000000..9b6f563
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
@@ -0,0 +1,300 @@
+/*****************************************************************
+ * 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.remote;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
+import org.apache.cayenne.ResultIterator;
+import org.apache.cayenne.ResultIteratorCallback;
+import org.apache.cayenne.access.IncrementalFaultList;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.query.Ordering;
+import org.apache.cayenne.query.PrefetchTreeNode;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.QueryMetadataProxy;
+import org.apache.cayenne.query.QueryRouter;
+import org.apache.cayenne.query.SQLAction;
+import org.apache.cayenne.query.SQLActionVisitor;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.query.SortOrder;
+import org.apache.cayenne.util.XMLEncoder;
+
+/**
+ * A SelectQuery decorator that overrides the metadata to ensure that query
+ * result is cached on the server, so that subranges could be retrieved at a
+ * later time. Note that a special decorator that is a subclass of SelectQuery
+ * is needed so that {@link IncrementalFaultList} on the server-side could apply
+ * SelectQuery-specific optimizations.
+ *
+ * @since 3.0
+ */
+class IncrementalSelectQuery<T> extends SelectQuery<T> {
+
+ private SelectQuery<T> query;
+ private String cacheKey;
+
+ IncrementalSelectQuery(SelectQuery<T> delegate, String cacheKey) {
+ this.query = delegate;
+ this.cacheKey = cacheKey;
+ }
+
+ @Override
+ public QueryMetadata getMetaData(EntityResolver resolver) {
+ final QueryMetadata metadata = query.getMetaData(resolver);
+
+ // the way paginated queries work on the server is that they are never
+ // cached
+ // (IncrementalFaultList interception happens before cache
+ // interception). So
+ // overriding caching settings in the metadata will only affect
+ // ClientServerChannel behavior
+ return new QueryMetadataProxy(metadata) {
+ public Query getOriginatingQuery() {
+ return null;
+ }
+
+ public String getCacheKey() {
+ return cacheKey;
+ }
+ };
+ }
+
+ @Override
+ public void addOrdering(Ordering ordering) {
+ query.addOrdering(ordering);
+ }
+
+ @Override
+ public void addOrdering(String sortPathSpec, SortOrder order) {
+ query.addOrdering(sortPathSpec, order);
+ }
+
+ @Override
+ public void addOrderings(Collection<? extends Ordering> orderings) {
+ query.addOrderings(orderings);
+ }
+
+ @Override
+ public PrefetchTreeNode addPrefetch(String prefetchPath) {
+ return query.addPrefetch(prefetchPath);
+ }
+
+ @Override
+ public void andQualifier(Expression e) {
+ query.andQualifier(e);
+ }
+
+ @Override
+ public void clearOrderings() {
+ query.clearOrderings();
+ }
+
+ @Override
+ public void clearPrefetches() {
+ query.clearPrefetches();
+ }
+
+ @Override
+ public SelectQuery<T> createQuery(Map<String, ?> parameters) {
+ return query.createQuery(parameters);
+ }
+
+ @Override
+ public SQLAction createSQLAction(SQLActionVisitor visitor) {
+ return query.createSQLAction(visitor);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return query.equals(obj);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ public String getCacheGroup() {
+ return super.getCacheGroup();
+ }
+
+ @Override
+ public int getFetchLimit() {
+ return query.getFetchLimit();
+ }
+
+ @Override
+ public List<Ordering> getOrderings() {
+ return query.getOrderings();
+ }
+
+ @Override
+ public int getPageSize() {
+ return query.getPageSize();
+ }
+
+ @Override
+ public PrefetchTreeNode getPrefetchTree() {
+ return query.getPrefetchTree();
+ }
+
+ @Override
+ public Expression getQualifier() {
+ return query.getQualifier();
+ }
+
+ @Override
+ public Object getRoot() {
+ return query.getRoot();
+ }
+
+ @Override
+ public int hashCode() {
+ return query.hashCode();
+ }
+
+ @Override
+ public void initWithProperties(Map<String, ?> properties) {
+ query.initWithProperties(properties);
+ }
+
+ @Override
+ public boolean isDistinct() {
+ return query.isDistinct();
+ }
+
+ @Override
+ public boolean isFetchingDataRows() {
+ return query.isFetchingDataRows();
+ }
+
+ @Override
+ public void orQualifier(Expression e) {
+ query.orQualifier(e);
+ }
+
+ @Override
+ public SelectQuery<T> queryWithParameters(Map<String, ?> parameters, boolean pruneMissing) {
+ return query.queryWithParameters(parameters, pruneMissing);
+ }
+
+ @Override
+ public SelectQuery<T> queryWithParameters(Map<String, ?> parameters) {
+ return query.queryWithParameters(parameters);
+ }
+
+ @Override
+ public void removeOrdering(Ordering ordering) {
+ query.removeOrdering(ordering);
+ }
+
+ @Override
+ public void removePrefetch(String prefetchPath) {
+ query.removePrefetch(prefetchPath);
+ }
+
+ @Override
+ public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
+ query.route(router, resolver, substitutedQuery);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Override
+ public void setCacheGroup(String cacheGroup) {
+ query.setCacheGroup(cacheGroup);
+ }
+
+ @Override
+ public void setDistinct(boolean distinct) {
+ query.setDistinct(distinct);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void setFetchingDataRows(boolean flag) {
+ query.setFetchingDataRows(flag);
+ }
+
+ @Override
+ public void setFetchLimit(int fetchLimit) {
+ query.setFetchLimit(fetchLimit);
+ }
+
+ @Override
+ public void setPageSize(int pageSize) {
+ query.setPageSize(pageSize);
+ }
+
+ @Override
+ public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
+ query.setPrefetchTree(prefetchTree);
+ }
+
+ @Override
+ public void setQualifier(Expression qualifier) {
+ query.setQualifier(qualifier);
+ }
+
+ @Override
+ public void setRoot(Object value) {
+ query.setRoot(value);
+ }
+
+ @Override
+ public String toString() {
+ return query.toString();
+ }
+
+ @Override
+ public List<T> select(ObjectContext context) {
+ return query.select(context);
+ }
+
+ @Override
+ public T selectOne(ObjectContext context) {
+ return query.selectOne(context);
+ }
+
+ @Override
+ public T selectFirst(ObjectContext context) {
+ return query.selectFirst(context);
+ }
+
+ @Override
+ public void iterate(ObjectContext context, ResultIteratorCallback<T> callback) {
+ query.iterate(context, callback);
+ }
+
+ @Override
+ public ResultIterator<T> iterator(ObjectContext context) {
+ return query.iterator(context);
+ }
+
+ @Override
+ public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+ return query.batchIterator(context, size);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
new file mode 100644
index 0000000..3c8b4d5
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/QueryMessage.java
@@ -0,0 +1,55 @@
+/*****************************************************************
+ * 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.remote;
+
+import org.apache.cayenne.query.Query;
+
+/**
+ * A message passed to a DataChannel to request a query execution with result returned as
+ * QueryResponse.
+ *
+ * @since 1.2
+ */
+public class QueryMessage implements ClientMessage {
+
+ protected Query query;
+
+ // for hessian serialization
+ @SuppressWarnings("unused")
+ private QueryMessage() {
+
+ }
+
+ public QueryMessage(Query query) {
+ this.query = query;
+ }
+
+ public Query getQuery() {
+ return query;
+ }
+
+ /**
+ * Returns a description of the type of message. In this case always "Query".
+ */
+ @Override
+ public String toString() {
+ return "Query";
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/38f37d79/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
new file mode 100644
index 0000000..f2b5e78
--- /dev/null
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/remote/RangeQuery.java
@@ -0,0 +1,156 @@
+/*****************************************************************
+ * 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.remote;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.query.Query;
+import org.apache.cayenne.query.QueryCacheStrategy;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.QueryMetadataProxy;
+import org.apache.cayenne.query.QueryRouter;
+import org.apache.cayenne.query.SQLAction;
+import org.apache.cayenne.query.SQLActionVisitor;
+import org.apache.cayenne.reflect.ClassDescriptor;
+
+/**
+ * A Query that fetches a range of objects from a previously fetched server-side paginated
+ * list. This query is client-only and can't be executed on the server.
+ *
+ * @since 1.2
+ */
+class RangeQuery implements Query {
+
+ private String cacheKey;
+ private int fetchOffset;
+ private int fetchLimit;
+ private Query originatingQuery;
+
+ // exists for hessian serialization.
+ @SuppressWarnings("unused")
+ private RangeQuery() {
+
+ }
+
+ /**
+ * Creates a query that returns a single page from an existing cached server-side
+ * result list.
+ */
+ RangeQuery(String cacheKey, int fetchStartIndex, int fetchLimit,
+ Query originatingQuery) {
+ this.cacheKey = cacheKey;
+ this.fetchOffset = fetchStartIndex;
+ this.fetchLimit = fetchLimit;
+ this.originatingQuery = originatingQuery;
+ }
+
+ public QueryMetadata getMetaData(EntityResolver resolver) {
+ final QueryMetadata originatingMetadata = originatingQuery.getMetaData(resolver);
+
+ return new QueryMetadataProxy(originatingMetadata) {
+
+ public Query getOriginatingQuery() {
+ return originatingQuery;
+ }
+
+ public List<Object> getResultSetMapping() {
+ return null;
+ }
+
+ public boolean isSingleResultSetMapping() {
+ return false;
+ }
+
+ public String getCacheKey() {
+ return cacheKey;
+ }
+
+ public String getCacheGroup() {
+ return null;
+ }
+
+ public int getFetchOffset() {
+ return fetchOffset;
+ }
+
+ public int getFetchLimit() {
+ return fetchLimit;
+ }
+
+ public int getPageSize() {
+ return 0;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public QueryCacheStrategy getCacheStrategy() {
+ return QueryCacheStrategy.getDefaultStrategy();
+ }
+
+ public DataMap getDataMap() {
+ throw new UnsupportedOperationException();
+ }
+
+ public DbEntity getDbEntity() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ObjEntity getObjEntity() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ClassDescriptor getClassDescriptor() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Procedure getProcedure() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Map<String, String> getPathSplitAliases() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isRefreshingObjects() {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getStatementFetchSize() {
+ return 0;
+ }
+ };
+ }
+
+ public SQLAction createSQLAction(SQLActionVisitor visitor) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
+ throw new UnsupportedOperationException();
+ }
+
+}