cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject [06/12] cayenne git commit: CAY-2026 Java 7
Date Sat, 12 Sep 2015 10:41:09 GMT
http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
index 4aaab66..55d653d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
@@ -176,7 +176,7 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
 	public SQLSelect<T> params(Map<String, ?> parameters) {
 
 		if (this.params == null) {
-			this.params = new HashMap<String, Object>(parameters);
+			this.params = new HashMap<>(parameters);
 		} else {
 			Map bareMap = parameters;
 			this.params.putAll(bareMap);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java
index e569578..1c75a5b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLTemplate.java
@@ -398,7 +398,7 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery, XM
 		query.setDefaultTemplate(getDefaultTemplate());
 
 		if (templates != null) {
-			query.templates = new HashMap<String, String>(templates);
+			query.templates = new HashMap<>(templates);
 		}
 
 		query.metaData.copyFromInfo(this.metaData);
@@ -424,7 +424,7 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery, XM
 		query.setDefaultTemplate(getDefaultTemplate());
 
 		if (templates != null) {
-			query.templates = new HashMap<String, String>(templates);
+			query.templates = new HashMap<>(templates);
 		}
 
 		query.metaData.copyFromInfo(this.metaData);
@@ -579,7 +579,7 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery, XM
 	 */
 	public synchronized void setTemplate(String key, String template) {
 		if (templates == null) {
-			templates = new HashMap<String, String>();
+			templates = new HashMap<>();
 		}
 
 		templates.put(key, template);
@@ -648,7 +648,7 @@ public class SQLTemplate extends AbstractQuery implements ParameterizedQuery, XM
 			// are not serializable with Hessian...
 			this.parameters = new Map[parameters.length];
 			for (int i = 0; i < parameters.length; i++) {
-				this.parameters[i] = parameters[i] != null ? new HashMap<String, Object>(parameters[i])
+				this.parameters[i] = parameters[i] != null ? new HashMap<>(parameters[i])
 						: new HashMap<String, Object>();
 			}
 		}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
index a05c987..73424bd 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
@@ -40,7 +40,7 @@ class SelectQueryMetadata extends BaseQueryMetadata {
 	@Override
 	void copyFromInfo(QueryMetadata info) {
 		super.copyFromInfo(info);
-		this.pathSplitAliases = new HashMap<String, String>(info.getPathSplitAliases());
+		this.pathSplitAliases = new HashMap<>(info.getPathSplitAliases());
 	}
 
 	<T> boolean resolve(Object root, EntityResolver resolver, SelectQuery<T> query) {
@@ -124,7 +124,7 @@ class SelectQueryMetadata extends BaseQueryMetadata {
 		Map<String, String> aliases = expression.getPathAliases();
 		if (!aliases.isEmpty()) {
 			if (pathSplitAliases == null) {
-				pathSplitAliases = new HashMap<String, String>();
+				pathSplitAliases = new HashMap<>();
 			}
 
 			pathSplitAliases.putAll(aliases);
@@ -160,7 +160,7 @@ class SelectQueryMetadata extends BaseQueryMetadata {
 		}
 
 		if (pathSplitAliases == null) {
-			pathSplitAliases = new HashMap<String, String>();
+			pathSplitAliases = new HashMap<>();
 		}
 
 		for (String alias : aliases) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptorMap.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptorMap.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptorMap.java
index fa6b68c..c5d42e9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptorMap.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptorMap.java
@@ -43,9 +43,9 @@ public class ClassDescriptorMap {
     protected List<ClassDescriptorFactory> factories;
 
     public ClassDescriptorMap(EntityResolver resolver) {
-        this.descriptors = new HashMap<String, ClassDescriptor>();
+        this.descriptors = new HashMap<>();
         this.resolver = resolver;
-        this.factories = new ArrayList<ClassDescriptorFactory>();
+        this.factories = new ArrayList<>();
     }
 
     public EntityResolver getResolver() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
index 508d1e8..53395cb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
@@ -41,8 +41,8 @@ class LifecycleCallbackEventHandler {
 
     LifecycleCallbackEventHandler(EntityResolver resolver) {
         this.resolver = resolver;
-        this.listeners = new HashMap<String, Collection<AbstractCallback>>();
-        this.defaultListeners = new ArrayList<AbstractCallback>();
+        this.listeners = new HashMap<>();
+        this.defaultListeners = new ArrayList<>();
     }
 
     private boolean excludingDefaultListeners(String entityName) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
index ccb612d..9083f37 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
@@ -51,399 +51,400 @@ import org.apache.cayenne.util.Util;
  */
 public class LifecycleCallbackRegistry {
 
-    private EntityResolver entityResolver;
-    private LifecycleCallbackEventHandler[] eventCallbacks;
-    private Map<String, AnnotationReader> annotationsMap;
-    private Map<String, Collection<Class<?>>> entitiesByAnnotation;
-
-    /**
-     * Creates an empty callback registry.
-     */
-    public LifecycleCallbackRegistry(EntityResolver resolver) {
-
-        this.entityResolver = resolver;
-
-        // initialize callbacks map in constructor to avoid synchronization
-        // issues
-        // downstream.
-        this.eventCallbacks = new LifecycleCallbackEventHandler[LifecycleEvent.values().length];
-        for (int i = 0; i < eventCallbacks.length; i++) {
-            eventCallbacks[i] = new LifecycleCallbackEventHandler(resolver);
-        }
-
-        // other "static" lookup maps are initialized on-demand
-        this.entitiesByAnnotation = new ConcurrentHashMap<String, Collection<Class<?>>>();
-    }
-
-    /**
-     * Removes all listeners for all event types.
-     */
-    public void clear() {
-        for (LifecycleCallbackEventHandler eventCallback : eventCallbacks) {
-            eventCallback.clear();
-        }
-    }
-
-    /**
-     * Removes listeners for a single event type.
-     */
-    public void clear(int type) {
-        eventCallbacks[type].clear();
-    }
-
-    /**
-     * Returns true if there are no listeners for a specific event type.
-     */
-    public boolean isEmpty(LifecycleEvent type) {
-        return eventCallbacks[type.ordinal()].isEmpty();
-    }
-
-    /**
-     * Registers a {@link LifecycleListener} for all events on all entities.
-     * Note that listeners are not required to implement
-     * {@link LifecycleListener} interface. Other methods in this class can be
-     * used to register arbitrary listeners.
-     */
-    public void addDefaultListener(LifecycleListener listener) {
-        addDefaultListener(LifecycleEvent.POST_ADD, listener, "postAdd");
-        addDefaultListener(LifecycleEvent.PRE_PERSIST, listener, "prePersist");
-        addDefaultListener(LifecycleEvent.POST_PERSIST, listener, "postPersist");
-        addDefaultListener(LifecycleEvent.PRE_REMOVE, listener, "preRemove");
-        addDefaultListener(LifecycleEvent.POST_REMOVE, listener, "postRemove");
-        addDefaultListener(LifecycleEvent.PRE_UPDATE, listener, "preUpdate");
-        addDefaultListener(LifecycleEvent.POST_UPDATE, listener, "postUpdate");
-        addDefaultListener(LifecycleEvent.POST_LOAD, listener, "postLoad");
-    }
-
-    /**
-     * Registers a callback method to be invoked on a provided non-entity object
-     * when a lifecycle event occurs on any entity that does not suppress
-     * default callbacks.
-     */
-    public void addDefaultListener(LifecycleEvent type, Object listener, String methodName) {
-        eventCallbacks[type.ordinal()].addDefaultListener(listener, methodName);
-    }
-
-    /**
-     * Registers a {@link LifecycleListener} for all events on all entities.
-     * Note that listeners are not required to implement
-     * {@link LifecycleListener} interface. Other methods in this class can be
-     * used to register arbitrary listeners.
-     */
-    public void addListener(Class<?> entityClass, LifecycleListener listener) {
-        addListener(LifecycleEvent.POST_ADD, entityClass, listener, "postAdd");
-        addListener(LifecycleEvent.POST_PERSIST, entityClass, listener, "prePersist");
-        addListener(LifecycleEvent.POST_PERSIST, entityClass, listener, "postPersist");
-        addListener(LifecycleEvent.PRE_REMOVE, entityClass, listener, "preRemove");
-        addListener(LifecycleEvent.POST_REMOVE, entityClass, listener, "postRemove");
-        addListener(LifecycleEvent.PRE_UPDATE, entityClass, listener, "preUpdate");
-        addListener(LifecycleEvent.POST_UPDATE, entityClass, listener, "postUpdate");
-        addListener(LifecycleEvent.POST_LOAD, entityClass, listener, "postLoad");
-    }
-
-    /**
-     * Registers callback method to be invoked on a provided non-entity object
-     * when a lifecycle event occurs for a specific entity.
-     */
-    public void addListener(LifecycleEvent type, Class<?> entityClass, Object listener, String methodName) {
-        eventCallbacks[type.ordinal()].addListener(entityClass, listener, methodName);
-    }
-
-    /**
-     * Registers a callback method to be invoked on an entity class instances
-     * when a lifecycle event occurs.
-     * 
-     * @since 4.0
-     */
-    public void addCallback(LifecycleEvent type, Class<?> entityClass, String methodName) {
-        eventCallbacks[type.ordinal()].addListener(entityClass, methodName);
-    }
-    
-    /**
-     * @since 4.0 renamed to {@link #addCallback(LifecycleEvent, Class, String)}.
-     */
-    @Deprecated
-    public void addListener(LifecycleEvent type, Class<?> entityClass, String methodName) {
-        addCallback(type, entityClass, methodName);
-    }
-
-    /**
-     * Adds a listener, mapping its methods to events based on annotations.
-     * 
-     * @since 3.1
-     */
-    public void addListener(Object listener) {
-        if (listener == null) {
-            throw new NullPointerException("Null listener");
-        }
-
-        Class<?> listenerType = listener.getClass();
-        do {
-            for (Method m : listenerType.getDeclaredMethods()) {
-
-                for (Annotation a : m.getAnnotations()) {
-                    AnnotationReader reader = getAnnotationsMap().get(a.annotationType().getName());
-
-                    if (reader != null) {
-
-                        Set<Class<?>> types = new HashSet<Class<?>>();
-
-                        Class<?>[] entities = reader.entities(a);
-                        Class<? extends Annotation>[] entityAnnotations = reader.entityAnnotations(a);
-
-                        for (Class<?> type : entities) {
-                            // TODO: ignoring entity subclasses? whenever we add
-                            // those,
-                            // take
-                            // into account "exlcudeSuperclassListeners" flag
-                            types.add(type);
-                        }
-
-                        for (Class<? extends Annotation> type : entityAnnotations) {
-                            types.addAll(getAnnotatedEntities(type));
-                        }
-
-                        for (Class<?> type : types) {
-                            eventCallbacks[reader.eventType().ordinal()].addListener(type, listener, m);
-                        }
-
-                        // if no entities specified then adding global callback
-                        if (entities.length == 0 && entityAnnotations.length == 0) {
-                            eventCallbacks[reader.eventType().ordinal()].addDefaultListener(listener, m.getName());
-                        }
-                    }
-                }
-            }
-
-            listenerType = listenerType.getSuperclass();
-        } while (listenerType != null && !listenerType.equals(Object.class));
-    }
-
-    /**
-     * Invokes callbacks of a specific type for a given entity object.
-     */
-    public void performCallbacks(LifecycleEvent type, Persistent object) {
-        eventCallbacks[type.ordinal()].performCallbacks(object);
-    }
-
-    /**
-     * Invokes callbacks of a specific type for a collection of entity objects.
-     */
-    public void performCallbacks(LifecycleEvent type, Collection<?> objects) {
-        eventCallbacks[type.ordinal()].performCallbacks(objects);
-    }
-
-    // used by unit tests to poke inside the registry
-    LifecycleCallbackEventHandler getHandler(LifecycleEvent type) {
-        return eventCallbacks[type.ordinal()];
-    }
-
-    private Map<String, AnnotationReader> getAnnotationsMap() {
-        if (annotationsMap == null) {
-
-            Map<String, AnnotationReader> annotationsMap = new HashMap<String, AnnotationReader>();
-            annotationsMap.put(PostAdd.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.POST_ADD;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PostAdd) a).entityAnnotations();
-                }
-
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PostAdd) a).value();
-                }
-            });
-
-            annotationsMap.put(PrePersist.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.PRE_PERSIST;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PrePersist) a).entityAnnotations();
-                }
-
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PrePersist) a).value();
-                }
-            });
-
-            annotationsMap.put(PreRemove.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.PRE_REMOVE;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PreRemove) a).entityAnnotations();
-                }
-
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PreRemove) a).value();
-                }
-            });
-
-            annotationsMap.put(PreUpdate.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.PRE_UPDATE;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PreUpdate) a).entityAnnotations();
-                }
-
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PreUpdate) a).value();
-                }
-            });
-
-            annotationsMap.put(PostLoad.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.POST_LOAD;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PostLoad) a).entityAnnotations();
-                }
-
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PostLoad) a).value();
-                }
-            });
-
-            annotationsMap.put(PostPersist.class.getName(), new AnnotationReader() {
-
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.POST_PERSIST;
-                }
-
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PostPersist) a).entityAnnotations();
-                }
+	private EntityResolver entityResolver;
+	private LifecycleCallbackEventHandler[] eventCallbacks;
+	private Map<String, AnnotationReader> annotationsMap;
+	private Map<String, Collection<Class<?>>> entitiesByAnnotation;
+
+	/**
+	 * Creates an empty callback registry.
+	 */
+	public LifecycleCallbackRegistry(EntityResolver resolver) {
+
+		this.entityResolver = resolver;
+
+		// initialize callbacks map in constructor to avoid synchronization
+		// issues
+		// downstream.
+		this.eventCallbacks = new LifecycleCallbackEventHandler[LifecycleEvent.values().length];
+		for (int i = 0; i < eventCallbacks.length; i++) {
+			eventCallbacks[i] = new LifecycleCallbackEventHandler(resolver);
+		}
+
+		// other "static" lookup maps are initialized on-demand
+		this.entitiesByAnnotation = new ConcurrentHashMap<>();
+	}
+
+	/**
+	 * Removes all listeners for all event types.
+	 */
+	public void clear() {
+		for (LifecycleCallbackEventHandler eventCallback : eventCallbacks) {
+			eventCallback.clear();
+		}
+	}
+
+	/**
+	 * Removes listeners for a single event type.
+	 */
+	public void clear(int type) {
+		eventCallbacks[type].clear();
+	}
+
+	/**
+	 * Returns true if there are no listeners for a specific event type.
+	 */
+	public boolean isEmpty(LifecycleEvent type) {
+		return eventCallbacks[type.ordinal()].isEmpty();
+	}
+
+	/**
+	 * Registers a {@link LifecycleListener} for all events on all entities.
+	 * Note that listeners are not required to implement
+	 * {@link LifecycleListener} interface. Other methods in this class can be
+	 * used to register arbitrary listeners.
+	 */
+	public void addDefaultListener(LifecycleListener listener) {
+		addDefaultListener(LifecycleEvent.POST_ADD, listener, "postAdd");
+		addDefaultListener(LifecycleEvent.PRE_PERSIST, listener, "prePersist");
+		addDefaultListener(LifecycleEvent.POST_PERSIST, listener, "postPersist");
+		addDefaultListener(LifecycleEvent.PRE_REMOVE, listener, "preRemove");
+		addDefaultListener(LifecycleEvent.POST_REMOVE, listener, "postRemove");
+		addDefaultListener(LifecycleEvent.PRE_UPDATE, listener, "preUpdate");
+		addDefaultListener(LifecycleEvent.POST_UPDATE, listener, "postUpdate");
+		addDefaultListener(LifecycleEvent.POST_LOAD, listener, "postLoad");
+	}
+
+	/**
+	 * Registers a callback method to be invoked on a provided non-entity object
+	 * when a lifecycle event occurs on any entity that does not suppress
+	 * default callbacks.
+	 */
+	public void addDefaultListener(LifecycleEvent type, Object listener, String methodName) {
+		eventCallbacks[type.ordinal()].addDefaultListener(listener, methodName);
+	}
+
+	/**
+	 * Registers a {@link LifecycleListener} for all events on all entities.
+	 * Note that listeners are not required to implement
+	 * {@link LifecycleListener} interface. Other methods in this class can be
+	 * used to register arbitrary listeners.
+	 */
+	public void addListener(Class<?> entityClass, LifecycleListener listener) {
+		addListener(LifecycleEvent.POST_ADD, entityClass, listener, "postAdd");
+		addListener(LifecycleEvent.POST_PERSIST, entityClass, listener, "prePersist");
+		addListener(LifecycleEvent.POST_PERSIST, entityClass, listener, "postPersist");
+		addListener(LifecycleEvent.PRE_REMOVE, entityClass, listener, "preRemove");
+		addListener(LifecycleEvent.POST_REMOVE, entityClass, listener, "postRemove");
+		addListener(LifecycleEvent.PRE_UPDATE, entityClass, listener, "preUpdate");
+		addListener(LifecycleEvent.POST_UPDATE, entityClass, listener, "postUpdate");
+		addListener(LifecycleEvent.POST_LOAD, entityClass, listener, "postLoad");
+	}
+
+	/**
+	 * Registers callback method to be invoked on a provided non-entity object
+	 * when a lifecycle event occurs for a specific entity.
+	 */
+	public void addListener(LifecycleEvent type, Class<?> entityClass, Object listener, String methodName) {
+		eventCallbacks[type.ordinal()].addListener(entityClass, listener, methodName);
+	}
+
+	/**
+	 * Registers a callback method to be invoked on an entity class instances
+	 * when a lifecycle event occurs.
+	 * 
+	 * @since 4.0
+	 */
+	public void addCallback(LifecycleEvent type, Class<?> entityClass, String methodName) {
+		eventCallbacks[type.ordinal()].addListener(entityClass, methodName);
+	}
+
+	/**
+	 * @since 4.0 renamed to {@link #addCallback(LifecycleEvent, Class, String)}
+	 *        .
+	 */
+	@Deprecated
+	public void addListener(LifecycleEvent type, Class<?> entityClass, String methodName) {
+		addCallback(type, entityClass, methodName);
+	}
+
+	/**
+	 * Adds a listener, mapping its methods to events based on annotations.
+	 * 
+	 * @since 3.1
+	 */
+	public void addListener(Object listener) {
+		if (listener == null) {
+			throw new NullPointerException("Null listener");
+		}
+
+		Class<?> listenerType = listener.getClass();
+		do {
+			for (Method m : listenerType.getDeclaredMethods()) {
+
+				for (Annotation a : m.getAnnotations()) {
+					AnnotationReader reader = getAnnotationsMap().get(a.annotationType().getName());
+
+					if (reader != null) {
+
+						Set<Class<?>> types = new HashSet<Class<?>>();
+
+						Class<?>[] entities = reader.entities(a);
+						Class<? extends Annotation>[] entityAnnotations = reader.entityAnnotations(a);
+
+						for (Class<?> type : entities) {
+							// TODO: ignoring entity subclasses? whenever we add
+							// those,
+							// take
+							// into account "exlcudeSuperclassListeners" flag
+							types.add(type);
+						}
+
+						for (Class<? extends Annotation> type : entityAnnotations) {
+							types.addAll(getAnnotatedEntities(type));
+						}
+
+						for (Class<?> type : types) {
+							eventCallbacks[reader.eventType().ordinal()].addListener(type, listener, m);
+						}
+
+						// if no entities specified then adding global callback
+						if (entities.length == 0 && entityAnnotations.length == 0) {
+							eventCallbacks[reader.eventType().ordinal()].addDefaultListener(listener, m.getName());
+						}
+					}
+				}
+			}
+
+			listenerType = listenerType.getSuperclass();
+		} while (listenerType != null && !listenerType.equals(Object.class));
+	}
+
+	/**
+	 * Invokes callbacks of a specific type for a given entity object.
+	 */
+	public void performCallbacks(LifecycleEvent type, Persistent object) {
+		eventCallbacks[type.ordinal()].performCallbacks(object);
+	}
+
+	/**
+	 * Invokes callbacks of a specific type for a collection of entity objects.
+	 */
+	public void performCallbacks(LifecycleEvent type, Collection<?> objects) {
+		eventCallbacks[type.ordinal()].performCallbacks(objects);
+	}
+
+	// used by unit tests to poke inside the registry
+	LifecycleCallbackEventHandler getHandler(LifecycleEvent type) {
+		return eventCallbacks[type.ordinal()];
+	}
+
+	private Map<String, AnnotationReader> getAnnotationsMap() {
+		if (annotationsMap == null) {
+
+			Map<String, AnnotationReader> annotationsMap = new HashMap<>();
+			annotationsMap.put(PostAdd.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.POST_ADD;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PostAdd) a).entityAnnotations();
+				}
+
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PostAdd) a).value();
+				}
+			});
+
+			annotationsMap.put(PrePersist.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.PRE_PERSIST;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PrePersist) a).entityAnnotations();
+				}
+
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PrePersist) a).value();
+				}
+			});
+
+			annotationsMap.put(PreRemove.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.PRE_REMOVE;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PreRemove) a).entityAnnotations();
+				}
+
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PreRemove) a).value();
+				}
+			});
+
+			annotationsMap.put(PreUpdate.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.PRE_UPDATE;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PreUpdate) a).entityAnnotations();
+				}
+
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PreUpdate) a).value();
+				}
+			});
+
+			annotationsMap.put(PostLoad.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.POST_LOAD;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PostLoad) a).entityAnnotations();
+				}
+
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PostLoad) a).value();
+				}
+			});
+
+			annotationsMap.put(PostPersist.class.getName(), new AnnotationReader() {
+
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.POST_PERSIST;
+				}
+
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PostPersist) a).entityAnnotations();
+				}
 
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PostPersist) a).value();
-                }
-            });
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PostPersist) a).value();
+				}
+			});
 
-            annotationsMap.put(PostUpdate.class.getName(), new AnnotationReader() {
+			annotationsMap.put(PostUpdate.class.getName(), new AnnotationReader() {
 
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.POST_UPDATE;
-                }
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.POST_UPDATE;
+				}
 
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PostUpdate) a).entityAnnotations();
-                }
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PostUpdate) a).entityAnnotations();
+				}
 
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PostUpdate) a).value();
-                }
-            });
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PostUpdate) a).value();
+				}
+			});
 
-            annotationsMap.put(PostRemove.class.getName(), new AnnotationReader() {
+			annotationsMap.put(PostRemove.class.getName(), new AnnotationReader() {
 
-                @Override
-                LifecycleEvent eventType() {
-                    return LifecycleEvent.POST_REMOVE;
-                }
+				@Override
+				LifecycleEvent eventType() {
+					return LifecycleEvent.POST_REMOVE;
+				}
 
-                @Override
-                Class<? extends Annotation>[] entityAnnotations(Annotation a) {
-                    return ((PostRemove) a).entityAnnotations();
-                }
+				@Override
+				Class<? extends Annotation>[] entityAnnotations(Annotation a) {
+					return ((PostRemove) a).entityAnnotations();
+				}
 
-                @Override
-                Class<?>[] entities(Annotation a) {
-                    return ((PostRemove) a).value();
-                }
-            });
+				@Override
+				Class<?>[] entities(Annotation a) {
+					return ((PostRemove) a).value();
+				}
+			});
 
-            this.annotationsMap = annotationsMap;
-        }
+			this.annotationsMap = annotationsMap;
+		}
 
-        return annotationsMap;
-    }
+		return annotationsMap;
+	}
 
-    private Collection<Class<?>> getAnnotatedEntities(Class<? extends Annotation> annotationType) {
+	private Collection<Class<?>> getAnnotatedEntities(Class<? extends Annotation> annotationType) {
 
-        Collection<Class<?>> entities = entitiesByAnnotation.get(annotationType.getName());
+		Collection<Class<?>> entities = entitiesByAnnotation.get(annotationType.getName());
 
-        if (entities == null) {
+		if (entities == null) {
 
-            // ensure no dupes
-            entities = new HashSet<Class<?>>();
+			// ensure no dupes
+			entities = new HashSet<Class<?>>();
 
-            for (ObjEntity entity : entityResolver.getObjEntities()) {
-                Class<?> entityType;
-                try {
-                    entityType = Util.getJavaClass(entity.getClassName());
-                } catch (ClassNotFoundException e) {
-                    throw new CayenneRuntimeException("Class not found: " + entity.getClassName(), e);
-                }
+			for (ObjEntity entity : entityResolver.getObjEntities()) {
+				Class<?> entityType;
+				try {
+					entityType = Util.getJavaClass(entity.getClassName());
+				} catch (ClassNotFoundException e) {
+					throw new CayenneRuntimeException("Class not found: " + entity.getClassName(), e);
+				}
 
-                // ensure that we don't register the same callback for multiple
-                // classes in the same hierarchy, so find the topmost type using
-                // a given
-                // annotation and register it once
+				// ensure that we don't register the same callback for multiple
+				// classes in the same hierarchy, so find the topmost type using
+				// a given
+				// annotation and register it once
 
-                // TODO: This ignores "excludeSuperclassListeners" setting,
-                // which is
-                // not possible with annotations anyways
+				// TODO: This ignores "excludeSuperclassListeners" setting,
+				// which is
+				// not possible with annotations anyways
 
-                while (entityType != null && entityType.isAnnotationPresent(annotationType)) {
+				while (entityType != null && entityType.isAnnotationPresent(annotationType)) {
 
-                    Class<?> superType = entityType.getSuperclass();
+					Class<?> superType = entityType.getSuperclass();
 
-                    if (superType == null || !superType.isAnnotationPresent(annotationType)) {
-                        entities.add(entityType);
-                        break;
-                    }
+					if (superType == null || !superType.isAnnotationPresent(annotationType)) {
+						entities.add(entityType);
+						break;
+					}
 
-                    entityType = superType;
-                }
+					entityType = superType;
+				}
 
-            }
+			}
 
-            entitiesByAnnotation.put(annotationType.getName(), entities);
-        }
+			entitiesByAnnotation.put(annotationType.getName(), entities);
+		}
 
-        return entities;
-    }
+		return entities;
+	}
 
-    abstract class AnnotationReader {
+	abstract class AnnotationReader {
 
-        abstract LifecycleEvent eventType();
+		abstract LifecycleEvent eventType();
 
-        abstract Class<?>[] entities(Annotation a);
+		abstract Class<?>[] entities(Annotation a);
 
-        abstract Class<? extends Annotation>[] entityAnnotations(Annotation a);
-    }
+		abstract Class<? extends Annotation>[] entityAnnotations(Annotation a);
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
index 976fc6e..c34fb4b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
@@ -44,409 +44,401 @@ import org.apache.cayenne.map.ObjRelationship;
  */
 public class PersistentDescriptor implements ClassDescriptor {
 
-    static final Integer TRANSIENT_STATE = Integer.valueOf(PersistenceState.TRANSIENT);
-    static final Integer HOLLOW_STATE = Integer.valueOf(PersistenceState.HOLLOW);
-    static final Integer COMMITTED_STATE = Integer.valueOf(PersistenceState.COMMITTED);
-
-    protected ClassDescriptor superclassDescriptor;
-
-    // compiled properties ...
-    protected Class<?> objectClass;
-    protected Map<String, PropertyDescriptor> declaredProperties;
-    protected Map<String, PropertyDescriptor> properties;
-   
-    protected Map<String, ClassDescriptor> subclassDescriptors;
-    protected Accessor persistenceStateAccessor;
-
-    protected ObjEntity entity;
-    protected Collection<DbEntity> rootDbEntities;
-    protected EntityInheritanceTree entityInheritanceTree;
-
-    // combines declared and super properties
-    protected Collection<AttributeProperty> idProperties;
-
-    // combines declared and super properties
-    protected Collection<ArcProperty> mapArcProperties;
-
-    // inheritance information
-    protected Collection<ObjAttribute> allDiscriminatorColumns;
-    protected Expression entityQualifier;
-
-    /**
-     * Creates a PersistentDescriptor.
-     */
-    public PersistentDescriptor() {
-        this.declaredProperties = new HashMap<String, PropertyDescriptor>();
-        this.properties = new HashMap<String, PropertyDescriptor>();
-        this.subclassDescriptors = new HashMap<String, ClassDescriptor>();
-
-        // must be a set as duplicate addition attempts are expected...
-        this.rootDbEntities = new HashSet<DbEntity>(1);
-    }
-
-    public void setDiscriminatorColumns(Collection<ObjAttribute> columns) {
-        if (columns == null || columns.isEmpty()) {
-            allDiscriminatorColumns = null;
-        }
-        else {
-            allDiscriminatorColumns = new ArrayList<ObjAttribute>(columns);
-        }
-    }
-
-    /**
-     * Registers a superclass property.
-     */
-    public void addSuperProperty(PropertyDescriptor property) {
-        properties.put(property.getName(), property);
-        indexAddedProperty(property);
-    }
-
-    /**
-     * Registers a property. This method is useful to customize default ClassDescriptor
-     * generated from ObjEntity by adding new properties or overriding the standard ones.
-     */
-    public void addDeclaredProperty(PropertyDescriptor property) {
-        declaredProperties.put(property.getName(), property);
-        properties.put(property.getName(), property);
-        indexAddedProperty(property);
-    }
-
-    /**
-     * Adds a root DbEntity to the list of roots, filtering duplicates.
-     */
-    public void addRootDbEntity(DbEntity dbEntity) {
-        this.rootDbEntities.add(dbEntity);
-    }
-    
-    void sortProperties() {
-
-        // ensure properties are stored in predictable order per CAY-1729
-
-        // 'properties' is a superset of 'declaredProperties', so let's sort all
-        // properties, and populated both ordered collections at once
-        if (properties.size() > 1) {
-
-            List<Entry<String, PropertyDescriptor>> entries = new ArrayList<Entry<String, PropertyDescriptor>>(
-                    properties.entrySet());
-
-            Collections.sort(entries, PropertyComparator.comparator);
-
-            Map<String, PropertyDescriptor> orderedProperties = new LinkedHashMap<String, PropertyDescriptor>(
-                    (int) (entries.size() / 0.75));
-
-            Map<String, PropertyDescriptor> orderedDeclared = new LinkedHashMap<String, PropertyDescriptor>(
-                    (int) (declaredProperties.size() / 0.75));
-
-            for (Entry<String, PropertyDescriptor> e : entries) {
-                orderedProperties.put(e.getKey(), e.getValue());
-                
-                if (declaredProperties.containsKey(e.getKey())) {
-                    orderedDeclared.put(e.getKey(), e.getValue());
-                }
-            }
-
-            this.properties = orderedProperties;
-            this.declaredProperties = orderedDeclared;
-        }
-    }
-    
-    void indexAddedProperty(PropertyDescriptor property) {
-        if (property instanceof AttributeProperty) {
-
-            AttributeProperty attributeProperty = (AttributeProperty) property;
-            ObjAttribute attribute = attributeProperty.getAttribute();
-            if (attribute.isPrimaryKey()) {
-
-                if (idProperties == null) {
-                    idProperties = new ArrayList<AttributeProperty>(2);
-                }
-
-                idProperties.add(attributeProperty);
-            }
-        }
-        else if (property instanceof ArcProperty) {
-            ObjRelationship relationship = ((ArcProperty) property).getRelationship();
-            ObjRelationship reverseRelationship = relationship.getReverseRelationship();
-            if (reverseRelationship != null
-                    && "java.util.Map".equals(reverseRelationship.getCollectionType())) {
-
-                if (mapArcProperties == null) {
-                    mapArcProperties = new ArrayList<ArcProperty>(2);
-                }
-
-                mapArcProperties.add((ArcProperty) property);
-            }
-        }
-    }
-
-    /**
-     * Removes declared property. This method can be used to customize default
-     * ClassDescriptor generated from ObjEntity.
-     */
-    public void removeDeclaredProperty(String propertyName) {
-        Object removed = declaredProperties.remove(propertyName);
-
-        if (removed != null) {
-            if (idProperties != null) {
-                idProperties.remove(removed);
-            }
-
-            if (mapArcProperties != null) {
-                mapArcProperties.remove(removed);
-            }
-            
-            properties.remove(propertyName);
-        }
-    }
-
-    /**
-     * Adds a subclass descriptor that maps to a given class name.
-     */
-    public void addSubclassDescriptor(String className, ClassDescriptor subclassDescriptor) {
-        // note that 'className' should be used instead of
-        // "subclassDescriptor.getEntity().getClassName()", as this method is called in
-        // the early phases of descriptor initialization and we do not want to trigger
-        // subclassDescriptor resolution just yet to prevent stack overflow.
-        subclassDescriptors.put(className, subclassDescriptor);
-    }
-
-    public ObjEntity getEntity() {
-        return entity;
-    }
-
-    public Collection<DbEntity> getRootDbEntities() {
-        return rootDbEntities;
-    }
-
-    public boolean isFault(Object object) {
-        if (superclassDescriptor != null) {
-            return superclassDescriptor.isFault(object);
-        }
-
-        if (object == null) {
-            return false;
-        }
-
-        return HOLLOW_STATE.equals(persistenceStateAccessor.getValue(object));
-    }
-
-    public Class<?> getObjectClass() {
-        return objectClass;
-    }
-
-    void setObjectClass(Class<?> objectClass) {
-        this.objectClass = objectClass;
-    }
-
-    public ClassDescriptor getSubclassDescriptor(Class<?> objectClass) {
-        if (objectClass == null) {
-            throw new IllegalArgumentException("Null objectClass");
-        }
-
-        if (subclassDescriptors.isEmpty()) {
-            return this;
-        }
-
-        ClassDescriptor subclassDescriptor = subclassDescriptors.get(objectClass
-                .getName());
-
-        // ascend via the class hierarchy (only doing it if there are multiple choices)
-        if (subclassDescriptor == null) {
-            Class<?> currentClass = objectClass;
-            while (subclassDescriptor == null
-                    && (currentClass = currentClass.getSuperclass()) != null) {
-                subclassDescriptor = subclassDescriptors.get(currentClass.getName());
-            }
-        }
-
-        return subclassDescriptor != null ? subclassDescriptor : this;
-    }
-
-    public Collection<ObjAttribute> getDiscriminatorColumns() {
-        return allDiscriminatorColumns != null ? allDiscriminatorColumns : Collections
-                .<ObjAttribute> emptyList();
-    }
-
-    public Collection<AttributeProperty> getIdProperties() {
-
-        if (idProperties != null) {
-            return idProperties;
-        }
-
-        return Collections.emptyList();
-    }
-
-    public Collection<ArcProperty> getMapArcProperties() {
-
-        if (mapArcProperties != null) {
-            return mapArcProperties;
-        }
-
-        return Collections.EMPTY_LIST;
-    }
-
-    /**
-     * Recursively looks up property descriptor in this class descriptor and all
-     * superclass descriptors.
-     */
-    public PropertyDescriptor getProperty(String propertyName) {
-        PropertyDescriptor property = getDeclaredProperty(propertyName);
-
-        if (property == null && superclassDescriptor != null) {
-            property = superclassDescriptor.getProperty(propertyName);
-        }
-
-        return property;
-    }
-
-    public PropertyDescriptor getDeclaredProperty(String propertyName) {
-        return declaredProperties.get(propertyName);
-    }
-
-    /**
-     * Returns a descriptor of the mapped superclass or null if the descriptor's entity
-     * sits at the top of inheritance hierarchy.
-     */
-    public ClassDescriptor getSuperclassDescriptor() {
-        return superclassDescriptor;
-    }
-
-    /**
-     * Creates a new instance of a class described by this object.
-     */
-    public Object createObject() {
-        if (objectClass == null) {
-            throw new NullPointerException(
-                    "Null objectClass. Descriptor wasn't initialized properly.");
-        }
-
-        try {
-            return objectClass.newInstance();
-        }
-        catch (Throwable e) {
-            throw new CayenneRuntimeException("Error creating object of class '"
-                    + objectClass.getName()
-                    + "'", e);
-        }
-    }
-
-    /**
-     * Invokes 'prepareForAccess' of a super descriptor and then invokes
-     * 'prepareForAccess' of each declared property.
-     */
-    public void injectValueHolders(Object object) throws PropertyException {
-
-        // do super first
-        if (getSuperclassDescriptor() != null) {
-            getSuperclassDescriptor().injectValueHolders(object);
-        }
-
-        for (PropertyDescriptor property : declaredProperties.values()) {
-            property.injectValueHolder(object);
-        }
-    }
-
-    /**
-     * Copies object properties from one object to another. Invokes 'shallowCopy' of a
-     * super descriptor and then invokes 'shallowCopy' of each declared property.
-     */
-    public void shallowMerge(final Object from, final Object to) throws PropertyException {
-
-        visitProperties(new PropertyVisitor() {
-
-            public boolean visitAttribute(AttributeProperty property) {
-                property.writePropertyDirectly(
-                        to,
-                        property.readPropertyDirectly(to),
-                        property.readPropertyDirectly(from));
-                return true;
-            }
-
-            public boolean visitToOne(ToOneProperty property) {
-                property.invalidate(to);
-                return true;
-            }
-
-            public boolean visitToMany(ToManyProperty property) {
-                return true;
-            }
-        });
-    }
-
-    /**
-     * @since 3.0
-     */
-    public boolean visitDeclaredProperties(PropertyVisitor visitor) {
-
-        for (PropertyDescriptor next : declaredProperties.values()) {
-            if (!next.visit(visitor)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * @since 3.0
-     */
-    public boolean visitAllProperties(PropertyVisitor visitor) {
-        if (!visitProperties(visitor)) {
-            return false;
-        }
-
-        if (!subclassDescriptors.isEmpty()) {
-            for (ClassDescriptor next : subclassDescriptors.values()) {
-                if (!next.visitDeclaredProperties(visitor)) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    public boolean visitProperties(PropertyVisitor visitor) {
-        
-        for (PropertyDescriptor next : properties.values()) {
-            if (!next.visit(visitor)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public void setPersistenceStateAccessor(Accessor persistenceStateAccessor) {
-        this.persistenceStateAccessor = persistenceStateAccessor;
-    }
-
-    public void setEntity(ObjEntity entity) {
-        this.entity = entity;
-    }
-
-    public void setSuperclassDescriptor(ClassDescriptor superclassDescriptor) {
-        this.superclassDescriptor = superclassDescriptor;
-    }
-
-    public Expression getEntityQualifier() {
-        return entityQualifier;
-    }
-
-    public void setEntityQualifier(Expression entityQualifier) {
-        this.entityQualifier = entityQualifier;
-    }
-
-    public EntityInheritanceTree getEntityInheritanceTree() {
-        return entityInheritanceTree;
-    }
-
-    public void setEntityInheritanceTree(EntityInheritanceTree entityInheritanceTree) {
-        this.entityInheritanceTree = entityInheritanceTree;
-    }
-
-    public boolean hasSubclasses() {
-        return entityInheritanceTree != null
-                && !entityInheritanceTree.getChildren().isEmpty();
-    }
+	static final Integer TRANSIENT_STATE = Integer.valueOf(PersistenceState.TRANSIENT);
+	static final Integer HOLLOW_STATE = Integer.valueOf(PersistenceState.HOLLOW);
+	static final Integer COMMITTED_STATE = Integer.valueOf(PersistenceState.COMMITTED);
+
+	protected ClassDescriptor superclassDescriptor;
+
+	// compiled properties ...
+	protected Class<?> objectClass;
+	protected Map<String, PropertyDescriptor> declaredProperties;
+	protected Map<String, PropertyDescriptor> properties;
+
+	protected Map<String, ClassDescriptor> subclassDescriptors;
+	protected Accessor persistenceStateAccessor;
+
+	protected ObjEntity entity;
+	protected Collection<DbEntity> rootDbEntities;
+	protected EntityInheritanceTree entityInheritanceTree;
+
+	// combines declared and super properties
+	protected Collection<AttributeProperty> idProperties;
+
+	// combines declared and super properties
+	protected Collection<ArcProperty> mapArcProperties;
+
+	// inheritance information
+	protected Collection<ObjAttribute> allDiscriminatorColumns;
+	protected Expression entityQualifier;
+
+	/**
+	 * Creates a PersistentDescriptor.
+	 */
+	public PersistentDescriptor() {
+		this.declaredProperties = new HashMap<>();
+		this.properties = new HashMap<>();
+		this.subclassDescriptors = new HashMap<>();
+
+		// must be a set as duplicate addition attempts are expected...
+		this.rootDbEntities = new HashSet<DbEntity>(1);
+	}
+
+	public void setDiscriminatorColumns(Collection<ObjAttribute> columns) {
+		if (columns == null || columns.isEmpty()) {
+			allDiscriminatorColumns = null;
+		} else {
+			allDiscriminatorColumns = new ArrayList<ObjAttribute>(columns);
+		}
+	}
+
+	/**
+	 * Registers a superclass property.
+	 */
+	public void addSuperProperty(PropertyDescriptor property) {
+		properties.put(property.getName(), property);
+		indexAddedProperty(property);
+	}
+
+	/**
+	 * Registers a property. This method is useful to customize default
+	 * ClassDescriptor generated from ObjEntity by adding new properties or
+	 * overriding the standard ones.
+	 */
+	public void addDeclaredProperty(PropertyDescriptor property) {
+		declaredProperties.put(property.getName(), property);
+		properties.put(property.getName(), property);
+		indexAddedProperty(property);
+	}
+
+	/**
+	 * Adds a root DbEntity to the list of roots, filtering duplicates.
+	 */
+	public void addRootDbEntity(DbEntity dbEntity) {
+		this.rootDbEntities.add(dbEntity);
+	}
+
+	void sortProperties() {
+
+		// ensure properties are stored in predictable order per CAY-1729
+
+		// 'properties' is a superset of 'declaredProperties', so let's sort all
+		// properties, and populated both ordered collections at once
+		if (properties.size() > 1) {
+
+			List<Entry<String, PropertyDescriptor>> entries = new ArrayList<Entry<String, PropertyDescriptor>>(
+					properties.entrySet());
+
+			Collections.sort(entries, PropertyComparator.comparator);
+
+			Map<String, PropertyDescriptor> orderedProperties = new LinkedHashMap<String, PropertyDescriptor>(
+					(int) (entries.size() / 0.75));
+
+			Map<String, PropertyDescriptor> orderedDeclared = new LinkedHashMap<String, PropertyDescriptor>(
+					(int) (declaredProperties.size() / 0.75));
+
+			for (Entry<String, PropertyDescriptor> e : entries) {
+				orderedProperties.put(e.getKey(), e.getValue());
+
+				if (declaredProperties.containsKey(e.getKey())) {
+					orderedDeclared.put(e.getKey(), e.getValue());
+				}
+			}
+
+			this.properties = orderedProperties;
+			this.declaredProperties = orderedDeclared;
+		}
+	}
+
+	void indexAddedProperty(PropertyDescriptor property) {
+		if (property instanceof AttributeProperty) {
+
+			AttributeProperty attributeProperty = (AttributeProperty) property;
+			ObjAttribute attribute = attributeProperty.getAttribute();
+			if (attribute.isPrimaryKey()) {
+
+				if (idProperties == null) {
+					idProperties = new ArrayList<AttributeProperty>(2);
+				}
+
+				idProperties.add(attributeProperty);
+			}
+		} else if (property instanceof ArcProperty) {
+			ObjRelationship relationship = ((ArcProperty) property).getRelationship();
+			ObjRelationship reverseRelationship = relationship.getReverseRelationship();
+			if (reverseRelationship != null && "java.util.Map".equals(reverseRelationship.getCollectionType())) {
+
+				if (mapArcProperties == null) {
+					mapArcProperties = new ArrayList<ArcProperty>(2);
+				}
+
+				mapArcProperties.add((ArcProperty) property);
+			}
+		}
+	}
+
+	/**
+	 * Removes declared property. This method can be used to customize default
+	 * ClassDescriptor generated from ObjEntity.
+	 */
+	public void removeDeclaredProperty(String propertyName) {
+		Object removed = declaredProperties.remove(propertyName);
+
+		if (removed != null) {
+			if (idProperties != null) {
+				idProperties.remove(removed);
+			}
+
+			if (mapArcProperties != null) {
+				mapArcProperties.remove(removed);
+			}
+
+			properties.remove(propertyName);
+		}
+	}
+
+	/**
+	 * Adds a subclass descriptor that maps to a given class name.
+	 */
+	public void addSubclassDescriptor(String className, ClassDescriptor subclassDescriptor) {
+		// note that 'className' should be used instead of
+		// "subclassDescriptor.getEntity().getClassName()", as this method is
+		// called in
+		// the early phases of descriptor initialization and we do not want to
+		// trigger
+		// subclassDescriptor resolution just yet to prevent stack overflow.
+		subclassDescriptors.put(className, subclassDescriptor);
+	}
+
+	public ObjEntity getEntity() {
+		return entity;
+	}
+
+	public Collection<DbEntity> getRootDbEntities() {
+		return rootDbEntities;
+	}
+
+	public boolean isFault(Object object) {
+		if (superclassDescriptor != null) {
+			return superclassDescriptor.isFault(object);
+		}
+
+		if (object == null) {
+			return false;
+		}
+
+		return HOLLOW_STATE.equals(persistenceStateAccessor.getValue(object));
+	}
+
+	public Class<?> getObjectClass() {
+		return objectClass;
+	}
+
+	void setObjectClass(Class<?> objectClass) {
+		this.objectClass = objectClass;
+	}
+
+	public ClassDescriptor getSubclassDescriptor(Class<?> objectClass) {
+		if (objectClass == null) {
+			throw new IllegalArgumentException("Null objectClass");
+		}
+
+		if (subclassDescriptors.isEmpty()) {
+			return this;
+		}
+
+		ClassDescriptor subclassDescriptor = subclassDescriptors.get(objectClass.getName());
+
+		// ascend via the class hierarchy (only doing it if there are multiple
+		// choices)
+		if (subclassDescriptor == null) {
+			Class<?> currentClass = objectClass;
+			while (subclassDescriptor == null && (currentClass = currentClass.getSuperclass()) != null) {
+				subclassDescriptor = subclassDescriptors.get(currentClass.getName());
+			}
+		}
+
+		return subclassDescriptor != null ? subclassDescriptor : this;
+	}
+
+	public Collection<ObjAttribute> getDiscriminatorColumns() {
+		return allDiscriminatorColumns != null ? allDiscriminatorColumns : Collections.<ObjAttribute> emptyList();
+	}
+
+	public Collection<AttributeProperty> getIdProperties() {
+
+		if (idProperties != null) {
+			return idProperties;
+		}
+
+		return Collections.emptyList();
+	}
+
+	public Collection<ArcProperty> getMapArcProperties() {
+
+		if (mapArcProperties != null) {
+			return mapArcProperties;
+		}
+
+		return Collections.EMPTY_LIST;
+	}
+
+	/**
+	 * Recursively looks up property descriptor in this class descriptor and all
+	 * superclass descriptors.
+	 */
+	public PropertyDescriptor getProperty(String propertyName) {
+		PropertyDescriptor property = getDeclaredProperty(propertyName);
+
+		if (property == null && superclassDescriptor != null) {
+			property = superclassDescriptor.getProperty(propertyName);
+		}
+
+		return property;
+	}
+
+	public PropertyDescriptor getDeclaredProperty(String propertyName) {
+		return declaredProperties.get(propertyName);
+	}
+
+	/**
+	 * Returns a descriptor of the mapped superclass or null if the descriptor's
+	 * entity sits at the top of inheritance hierarchy.
+	 */
+	public ClassDescriptor getSuperclassDescriptor() {
+		return superclassDescriptor;
+	}
+
+	/**
+	 * Creates a new instance of a class described by this object.
+	 */
+	public Object createObject() {
+		if (objectClass == null) {
+			throw new NullPointerException("Null objectClass. Descriptor wasn't initialized properly.");
+		}
+
+		try {
+			return objectClass.newInstance();
+		} catch (Throwable e) {
+			throw new CayenneRuntimeException("Error creating object of class '" + objectClass.getName() + "'", e);
+		}
+	}
+
+	/**
+	 * Invokes 'prepareForAccess' of a super descriptor and then invokes
+	 * 'prepareForAccess' of each declared property.
+	 */
+	public void injectValueHolders(Object object) throws PropertyException {
+
+		// do super first
+		if (getSuperclassDescriptor() != null) {
+			getSuperclassDescriptor().injectValueHolders(object);
+		}
+
+		for (PropertyDescriptor property : declaredProperties.values()) {
+			property.injectValueHolder(object);
+		}
+	}
+
+	/**
+	 * Copies object properties from one object to another. Invokes
+	 * 'shallowCopy' of a super descriptor and then invokes 'shallowCopy' of
+	 * each declared property.
+	 */
+	public void shallowMerge(final Object from, final Object to) throws PropertyException {
+
+		visitProperties(new PropertyVisitor() {
+
+			public boolean visitAttribute(AttributeProperty property) {
+				property.writePropertyDirectly(to, property.readPropertyDirectly(to),
+						property.readPropertyDirectly(from));
+				return true;
+			}
+
+			public boolean visitToOne(ToOneProperty property) {
+				property.invalidate(to);
+				return true;
+			}
+
+			public boolean visitToMany(ToManyProperty property) {
+				return true;
+			}
+		});
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	public boolean visitDeclaredProperties(PropertyVisitor visitor) {
+
+		for (PropertyDescriptor next : declaredProperties.values()) {
+			if (!next.visit(visitor)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	public boolean visitAllProperties(PropertyVisitor visitor) {
+		if (!visitProperties(visitor)) {
+			return false;
+		}
+
+		if (!subclassDescriptors.isEmpty()) {
+			for (ClassDescriptor next : subclassDescriptors.values()) {
+				if (!next.visitDeclaredProperties(visitor)) {
+					return false;
+				}
+			}
+		}
+
+		return true;
+	}
+
+	public boolean visitProperties(PropertyVisitor visitor) {
+
+		for (PropertyDescriptor next : properties.values()) {
+			if (!next.visit(visitor)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	public void setPersistenceStateAccessor(Accessor persistenceStateAccessor) {
+		this.persistenceStateAccessor = persistenceStateAccessor;
+	}
+
+	public void setEntity(ObjEntity entity) {
+		this.entity = entity;
+	}
+
+	public void setSuperclassDescriptor(ClassDescriptor superclassDescriptor) {
+		this.superclassDescriptor = superclassDescriptor;
+	}
+
+	public Expression getEntityQualifier() {
+		return entityQualifier;
+	}
+
+	public void setEntityQualifier(Expression entityQualifier) {
+		this.entityQualifier = entityQualifier;
+	}
+
+	public EntityInheritanceTree getEntityInheritanceTree() {
+		return entityInheritanceTree;
+	}
+
+	public void setEntityInheritanceTree(EntityInheritanceTree entityInheritanceTree) {
+		this.entityInheritanceTree = entityInheritanceTree;
+	}
+
+	public boolean hasSubclasses() {
+		return entityInheritanceTree != null && !entityInheritanceTree.getChildren().isEmpty();
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
index a2da071..9f0503d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
@@ -215,7 +215,7 @@ public abstract class PersistentDescriptorFactory implements ClassDescriptorFact
             // using map instead of a Set to collect attributes, as
             // ObjEntity.getAttribute may return a decorator for attribute on
             // each call, resulting in dupes
-            final Map<String, ObjAttribute> attributes = new HashMap<String, ObjAttribute>();
+            final Map<String, ObjAttribute> attributes = new HashMap<>();
             final DbEntity dbEntity = descriptor.getEntity().getDbEntity();
 
             qualifier.traverse(new TraversalHelper() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorResultMetadata.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorResultMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorResultMetadata.java
index 81cd88b..1f4ab53 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorResultMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorResultMetadata.java
@@ -30,59 +30,58 @@ import org.apache.cayenne.query.EntityResultSegment;
  */
 class PersistentDescriptorResultMetadata implements EntityResultSegment {
 
-    ClassDescriptor classDescriptor;
-    Map<String, String> fields;
-    Map<String, String> reverseFields;
+	ClassDescriptor classDescriptor;
+	Map<String, String> fields;
+	Map<String, String> reverseFields;
 
-    PersistentDescriptorResultMetadata(ClassDescriptor classDescriptor) {
-        this.classDescriptor = classDescriptor;
-        this.fields = new HashMap<String, String>();
-        this.reverseFields = new HashMap<String, String>();
-    }
+	PersistentDescriptorResultMetadata(ClassDescriptor classDescriptor) {
+		this.classDescriptor = classDescriptor;
+		this.fields = new HashMap<>();
+		this.reverseFields = new HashMap<String, String>();
+	}
 
-    public ClassDescriptor getClassDescriptor() {
-        return classDescriptor;
-    }
+	public ClassDescriptor getClassDescriptor() {
+		return classDescriptor;
+	}
 
-    public Map<String, String> getFields() {
-        return fields;
-    }
+	public Map<String, String> getFields() {
+		return fields;
+	}
 
-    public String getColumnPath(String resultSetLabel) {
-        return reverseFields.get(resultSetLabel);
-    }
+	public String getColumnPath(String resultSetLabel) {
+		return reverseFields.get(resultSetLabel);
+	}
 
-    void addObjectField(String attributeName, String column) {
-        ObjEntity entity = classDescriptor.getEntity();
+	void addObjectField(String attributeName, String column) {
+		ObjEntity entity = classDescriptor.getEntity();
 
-        ObjAttribute attribute = entity.getAttribute(attributeName);
-        fields.put(attribute.getDbAttributePath(), column);
-        reverseFields.put(column, attribute.getDbAttributePath());
-    }
+		ObjAttribute attribute = entity.getAttribute(attributeName);
+		fields.put(attribute.getDbAttributePath(), column);
+		reverseFields.put(column, attribute.getDbAttributePath());
+	}
 
-    /**
-     * Adds a result set column mapping for a single object property of a specified entity
-     * that may differ from the root entity if inheritance is involved.
-     */
-    void addObjectField(String entityName, String attributeName, String column) {
-        ObjEntity entity = classDescriptor.getEntity().getDataMap().getObjEntity(
-                entityName);
+	/**
+	 * Adds a result set column mapping for a single object property of a
+	 * specified entity that may differ from the root entity if inheritance is
+	 * involved.
+	 */
+	void addObjectField(String entityName, String attributeName, String column) {
+		ObjEntity entity = classDescriptor.getEntity().getDataMap().getObjEntity(entityName);
 
-        ObjAttribute attribute = entity.getAttribute(attributeName);
-        fields.put(attribute.getDbAttributePath(), column);
-        reverseFields.put(column, attribute.getDbAttributePath());
-    }
+		ObjAttribute attribute = entity.getAttribute(attributeName);
+		fields.put(attribute.getDbAttributePath(), column);
+		reverseFields.put(column, attribute.getDbAttributePath());
+	}
 
-    /**
-     * Adds a result set column mapping for a single DbAttribute.
-     */
-    void addDbField(String dbAttributeName, String column) {
-        fields.put(dbAttributeName, column);
-        reverseFields.put(column, dbAttributeName);
-    }
+	/**
+	 * Adds a result set column mapping for a single DbAttribute.
+	 */
+	void addDbField(String dbAttributeName, String column) {
+		fields.put(dbAttributeName, column);
+		reverseFields.put(column, dbAttributeName);
+	}
 
-    public int getColumnOffset() {
-        throw new UnsupportedOperationException(
-                "Column offset only makes sense in the context of a query");
-    }
+	public int getColumnOffset() {
+		throw new UnsupportedOperationException("Column offset only makes sense in the context of a query");
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/cayenne-server/src/main/java/org/apache/cayenne/reflect/PropertyUtils.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PropertyUtils.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PropertyUtils.java
index d06726d..f9d840f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PropertyUtils.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PropertyUtils.java
@@ -36,8 +36,8 @@ import org.apache.cayenne.map.Entity;
  */
 public class PropertyUtils {
 
-	private static final ConcurrentMap<String, Accessor> PATH_ACCESSORS = new ConcurrentHashMap<String, Accessor>();
-	private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Accessor>> SEGMENT_ACCESSORS = new ConcurrentHashMap<Class<?>, ConcurrentMap<String, Accessor>>();
+	private static final ConcurrentMap<String, Accessor> PATH_ACCESSORS = new ConcurrentHashMap<>();
+	private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Accessor>> SEGMENT_ACCESSORS = new ConcurrentHashMap<>();
 
 	/**
 	 * Compiles an accessor that can be used for fast access for the nested

http://git-wip-us.apache.org/repos/asf/cayenne/blob/13d0da53/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
index d04d163..2a63fbb 100644
--- 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
@@ -37,167 +37,155 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 /**
- * A generic implementation of an RemoteService. Can be subclassed to work with different
- * remoting mechanisms, such as Hessian or JAXRPC.
+ * 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 Log logger;
+	// keep logger non-static so that it could be garbage collected with this
+	// instance.
+	protected final Log logger;
 
-    protected ObjectContextFactory contextFactory;
-    protected String eventBridgeFactoryName;
-    protected Map<String, String> eventBridgeParameters;
+	protected ObjectContextFactory contextFactory;
+	protected String eventBridgeFactoryName;
+	protected Map<String, String> eventBridgeParameters;
 
-    /**
-     * @since 3.1
-     */
-    public BaseRemoteService(ObjectContextFactory contextFactory,
-            Map<String, String> eventBridgeProperties) {
+	/**
+	 * @since 3.1
+	 */
+	public BaseRemoteService(ObjectContextFactory contextFactory, Map<String, String> eventBridgeProperties) {
 
-        logger = LogFactory.getLog(getClass());
+		logger = LogFactory.getLog(getClass());
 
-        // start Cayenne service
-        logger.debug("ROP service is starting");
+		// start Cayenne service
+		logger.debug("ROP service is starting");
 
-        this.contextFactory = contextFactory;
-        initEventBridgeParameters(eventBridgeProperties);
+		this.contextFactory = contextFactory;
+		initEventBridgeParameters(eventBridgeProperties);
 
-        logger.debug(getClass().getName() + " started");
-    }
+		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();
-
-    public RemoteSession establishSession() {
-        logger.debug("Session requested by client");
-
-        RemoteSession session = createServerSession().getSession();
-
-        logger.debug("Established client session: " + session);
-        return session;
-    }
-
-    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();
-    }
-
-    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);
-        }
-    }
-
-    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<String, String>(
-                    properties);
-            eventBridgeParameters
-                    .remove(Constants.SERVER_ROP_EVENT_BRIDGE_FACTORY_PROPERTY);
-
-            this.eventBridgeFactoryName = eventBridgeFactoryName;
-            this.eventBridgeParameters = eventBridgeParameters;
-        }
-    }
+	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();
+
+	public RemoteSession establishSession() {
+		logger.debug("Session requested by client");
+
+		RemoteSession session = createServerSession().getSession();
+
+		logger.debug("Established client session: " + session);
+		return session;
+	}
+
+	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();
+	}
+
+	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);
+		}
+	}
+
+	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/13d0da53/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
index a1fc21e..80bd9bd 100644
--- 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
@@ -48,7 +48,7 @@ public abstract class HttpRemoteService extends BaseRemoteService {
     public HttpRemoteService(ObjectContextFactory contextFactory,
             Map<String, String> eventBridgeProperties) {
         super(contextFactory, eventBridgeProperties);
-        this.sharedChannels = new HashMap<String, WeakReference<DataChannel>>();
+        this.sharedChannels = new HashMap<>();
     }
 
     /**


Mime
View raw message