You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2014/05/09 19:34:30 UTC

[4/5] git commit: ISIS-550: PostsCollectionRemovedFromEvent implemented. Subscription Tests needed

ISIS-550: PostsCollectionRemovedFromEvent implemented. Subscription Tests needed


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

Branch: refs/heads/ISIS-550
Commit: f8833040119f72d41e0a7f7442725f4b2e118184
Parents: 1dac3ed
Author: Oscar Bou <os...@apache.org>
Authored: Fri May 9 13:47:23 2014 +0200
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Fri May 9 17:58:45 2014 +0100

----------------------------------------------------------------------
 .../event/PostsCollectionAddedToEventFacet.java |   2 -
 .../PostsCollectionRemovedFromEventFacet.java   |  35 ++++
 ...CollectionRemovedFromEventFacetAbstract.java |  55 ++++++
 ...tsCollectionAddedToEventFacetAnnotation.java |   1 -
 ...nRemovedFromEventAnnotationFacetFactory.java |  78 +++++++++
 ...llectionRemovedFromEventFacetAnnotation.java | 167 +++++++++++++++++++
 .../dflt/ProgrammingModelFacetsJava5.java       |   2 +
 ...emovedEventFacetAnnotationTest_newEvent.java |  50 ++++++
 .../dom/src/main/java/dom/todo/ToDoItem.java    |   9 +-
 .../java/dom/todo/ToDoItemSubscriptions.java    |   7 +
 .../ToDoItemTest_dependencies_addedEvent.java   |  85 ++++++++++
 11 files changed, 487 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionAddedToEventFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionAddedToEventFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionAddedToEventFacet.java
index 91d7d3c..72edf00 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionAddedToEventFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionAddedToEventFacet.java
@@ -21,8 +21,6 @@ package org.apache.isis.core.metamodel.facets.collections.event;
 
 import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent;
 import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
-import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.MultiTypedFacet;
 import org.apache.isis.core.metamodel.facets.PostsEventWithWrapperPolicy;
 import org.apache.isis.core.metamodel.facets.SingleValueFacet;

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacet.java
new file mode 100644
index 0000000..d351f84
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacet.java
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.collections.event;
+
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.core.metamodel.facetapi.MultiTypedFacet;
+import org.apache.isis.core.metamodel.facets.PostsEventWithWrapperPolicy;
+import org.apache.isis.core.metamodel.facets.SingleValueFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+
+/**
+ * Indicates that (the specified subclass of) {@link CollectionRemovedFromEvent} should be posted to the
+ * {@link EventBusService}.
+ */
+public interface PostsCollectionRemovedFromEventFacet extends SingleValueFacet<Class<? extends CollectionRemovedFromEvent<?,?>>>, CollectionRemoveFromFacet, MultiTypedFacet, PostsEventWithWrapperPolicy {
+}
+

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAbstract.java
new file mode 100644
index 0000000..a317467
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAbstract.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.isis.core.metamodel.facets.collections.event;
+
+import org.apache.isis.applib.annotation.WrapperPolicy;
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.SingleValueFacetAbstract;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+
+
+public abstract class PostsCollectionRemovedFromEventFacetAbstract 
+    extends SingleValueFacetAbstract<Class<? extends CollectionRemovedFromEvent<?,?>>> 
+    implements PostsCollectionRemovedFromEventFacet {
+
+	public static Class<? extends Facet> type() {
+	    
+	    // the "primary" type is CollectionAddToFacet rather than PostsRemovedFromCollectionEventFacet
+	    // so that this facet can wrap an existing (via setUnderlying).
+	    
+        //return PostsRemovedFromCollectionEventFacet.class;
+	    return CollectionRemoveFromFacet.class;
+    }
+
+    private final WrapperPolicy wrapperPolicy;
+
+    public PostsCollectionRemovedFromEventFacetAbstract(Class<? extends CollectionRemovedFromEvent<?, ?>> changedEventType, WrapperPolicy wrapperPolicy, FacetHolder holder) {
+        super(type(), changedEventType, holder);
+        this.wrapperPolicy = wrapperPolicy;
+    }
+
+    @Override
+    public WrapperPolicy getWrapperPolicy() {
+        return wrapperPolicy;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionAddedToEventFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionAddedToEventFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionAddedToEventFacetAnnotation.java
index f1e30ec..bd69339 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionAddedToEventFacetAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionAddedToEventFacetAnnotation.java
@@ -35,7 +35,6 @@ import org.apache.isis.core.metamodel.adapter.ServicesProvider;
 import org.apache.isis.core.metamodel.adapter.util.AdapterUtils;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facetapi.FacetUtil;
 import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.collections.event.PostsCollectionAddedToEventFacet;
 import org.apache.isis.core.metamodel.facets.collections.event.PostsCollectionAddedToEventFacetAbstract;

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventAnnotationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventAnnotationFacetFactory.java
new file mode 100644
index 0000000..67e8871
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventAnnotationFacetFactory.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.progmodel.facets.collections.event;
+
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.annotation.PostsCollectionRemovedFromEvent;
+import org.apache.isis.applib.annotation.WrapperPolicy;
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+import org.apache.isis.core.metamodel.adapter.ServicesProvider;
+import org.apache.isis.core.metamodel.adapter.ServicesProviderAware;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.facets.collections.event.PostsCollectionRemovedFromEventFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+
+public class PostsCollectionRemovedFromEventAnnotationFacetFactory extends FacetFactoryAbstract implements ServicesProviderAware {
+
+    private ServicesProvider servicesProvider;
+
+    public PostsCollectionRemovedFromEventAnnotationFacetFactory() {
+        super(FeatureType.COLLECTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+        final Method method = processMethodContext.getMethod();
+        FacetUtil.addFacet(create(method, processMethodContext.getFacetHolder()));
+    }
+
+    private PostsCollectionRemovedFromEventFacet create(Method method, final FacetHolder holder) {
+        final PostsCollectionRemovedFromEvent annotation = Annotations.getAnnotation(method, PostsCollectionRemovedFromEvent.class);
+        if(annotation == null) {
+            return null;
+        }
+        
+        final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class);
+        if(getterFacet == null) {
+            return null;
+        } 
+        final CollectionRemoveFromFacet collectionRemoveFromFacet = holder.getFacet(CollectionRemoveFromFacet.class);
+        if(collectionRemoveFromFacet == null) {
+            return null;
+        }
+        // the collectionRemoveFromFacet will end up as the underlying facet of the PostsCollectionRemovedFromEventFacetAnnotation
+
+        final Class<? extends CollectionRemovedFromEvent<?,?>> changedEventType = annotation.value();
+        final WrapperPolicy wrapperPolicy = annotation.wrapperPolicy();
+        return new PostsCollectionRemovedFromEventFacetAnnotation(changedEventType, wrapperPolicy, getterFacet, collectionRemoveFromFacet, servicesProvider, holder);
+    }
+
+    @Override
+    public void setServicesProvider(ServicesProvider servicesProvider) {
+        this.servicesProvider = servicesProvider;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAnnotation.java
new file mode 100644
index 0000000..a19b55d
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedFromEventFacetAnnotation.java
@@ -0,0 +1,167 @@
+/*
+ *  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.isis.core.progmodel.facets.collections.event;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.FatalException;
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.applib.annotation.WrapperPolicy;
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ServicesProvider;
+import org.apache.isis.core.metamodel.adapter.util.AdapterUtils;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.facets.collections.event.PostsCollectionRemovedFromEventFacet;
+import org.apache.isis.core.metamodel.facets.collections.event.PostsCollectionRemovedFromEventFacetAbstract;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+
+public class PostsCollectionRemovedFromEventFacetAnnotation extends
+		PostsCollectionRemovedFromEventFacetAbstract {
+
+	private final PropertyOrCollectionAccessorFacet getterFacet;
+	private final CollectionRemoveFromFacet collectionRemoveFromFacet;
+	private final ServicesProvider servicesProvider;
+
+	private EventBusService eventBusService;
+	private boolean searchedForEventBusService = false;
+
+	public PostsCollectionRemovedFromEventFacetAnnotation(
+			final Class<? extends CollectionRemovedFromEvent<?, ?>> eventType,
+			final WrapperPolicy wrapperPolicy,
+			final PropertyOrCollectionAccessorFacet getterFacet,
+			final CollectionRemoveFromFacet collectionRemoveFromFacet,
+			final ServicesProvider servicesProvider, final FacetHolder holder) {
+		super(eventType, wrapperPolicy, holder);
+		this.getterFacet = getterFacet;
+		this.collectionRemoveFromFacet = collectionRemoveFromFacet;
+		this.servicesProvider = servicesProvider;
+	}
+
+	@Override
+	public void remove(ObjectAdapter targetAdapter,
+			ObjectAdapter referencedObjectAdapter) {
+		if (this.collectionRemoveFromFacet == null) {
+			return;
+		}
+		eventBusService = getEventBusService();
+		if (eventBusService == null) {
+			collectionRemoveFromFacet.remove(targetAdapter,
+					referencedObjectAdapter);
+			return;
+		}
+
+		final Object referencedObject = AdapterUtils
+				.unwrap(referencedObjectAdapter);
+
+		// get hold of underlying collection
+		final Object collection = getterFacet.getProperty(targetAdapter);
+
+		// don't post event if the collections does not contain object
+		if (!((Collection<?>) collection).contains(referencedObject)) {
+			return;
+		}
+
+		// contains the element. So the event must be posted.
+		collectionRemoveFromFacet
+				.remove(targetAdapter, referencedObjectAdapter);
+
+		postEvent(targetAdapter, getIdentified().getIdentifier(),
+				referencedObject);
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	private void postEvent(final ObjectAdapter targetAdapter,
+			final Identifier identifier, final Object removedReference) {
+
+		final Object source = targetAdapter.getObject();
+		try {
+			final Class type = value();
+			final CollectionRemovedFromEvent<?, ?> event = newEvent(type, source,
+					identifier, removedReference);
+			eventBusService.post(event);
+		} catch (Exception e) {
+			throw new FatalException(e);
+		}
+	}
+
+	static <S, T> CollectionRemovedFromEvent<S, T> newEvent(
+			final Class<? extends CollectionRemovedFromEvent<S, T>> type,
+			final S source, final Identifier identifier, final T value)
+			throws InstantiationException, IllegalAccessException,
+			NoSuchFieldException {
+		final CollectionRemovedFromEvent<S, T> event = type.newInstance();
+
+		setField("source", event, source);
+		setField("identifier", event, identifier);
+		setField("value", event, value);
+		return event;
+	}
+
+	private static void setField(final String name,
+			final CollectionRemovedFromEvent<?, ?> event, final Object sourceValue)
+			throws NoSuchFieldException, IllegalAccessException {
+		final Field sourceField = CollectionRemovedFromEvent.class
+				.getDeclaredField(name);
+		sourceField.setAccessible(true);
+		sourceField.set(event, sourceValue);
+	}
+
+	private EventBusService getEventBusService() {
+		if (!searchedForEventBusService) {
+			final List<ObjectAdapter> serviceAdapters = servicesProvider
+					.getServices();
+			for (ObjectAdapter serviceAdapter : serviceAdapters) {
+				final Object service = serviceAdapter.getObject();
+				if (service instanceof EventBusService) {
+					eventBusService = (EventBusService) service;
+					break;
+				}
+			}
+		}
+		searchedForEventBusService = true;
+		return eventBusService;
+	}
+
+	// //////////////////////////////////////
+	// MultiTypedFacet
+	// //////////////////////////////////////
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public Class<? extends Facet>[] facetTypes() {
+		return Lists.newArrayList(type(), // ie CollectionRemoveFromFacet
+				PostsCollectionRemovedFromEventFacet.class).toArray(new Class[] {});
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T extends Facet> T getFacet(Class<T> facet) {
+		return (T) this;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
index 1e4598c..ca75555 100644
--- a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
+++ b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
@@ -48,6 +48,7 @@ import org.apache.isis.core.progmodel.facets.collections.clear.CollectionClearFa
 import org.apache.isis.core.progmodel.facets.collections.collection.CollectionFacetFactory;
 import org.apache.isis.core.progmodel.facets.collections.disabled.fromimmutable.DisabledFacetForCollectionDerivedFromImmutableTypeFacetFactory;
 import org.apache.isis.core.progmodel.facets.collections.event.PostsCollectionAddedToEventAnnotationFacetFactory;
+import org.apache.isis.core.progmodel.facets.collections.event.PostsCollectionRemovedFromEventAnnotationFacetFactory;
 import org.apache.isis.core.progmodel.facets.collections.modify.CollectionAddRemoveAndValidateFacetFactory;
 import org.apache.isis.core.progmodel.facets.collections.notpersisted.annotation.NotPersistedAnnotationForCollectionFacetFactory;
 import org.apache.isis.core.progmodel.facets.collections.sortedby.SortedByAnnotationFacetFactory;
@@ -366,6 +367,7 @@ public final class ProgrammingModelFacetsJava5 extends ProgrammingModelAbstract
         // must come after the property/collection/action accessor+mutator facet factories
         addFactory(PostsPropertyChangedEventAnnotationFacetFactory.class);
         addFactory(PostsCollectionAddedToEventAnnotationFacetFactory.class);
+        addFactory(PostsCollectionRemovedFromEventAnnotationFacetFactory.class);
 
         
         addFactory(ImmutableMarkerInterfaceFacetFactory.class);

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedEventFacetAnnotationTest_newEvent.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedEventFacetAnnotationTest_newEvent.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedEventFacetAnnotationTest_newEvent.java
new file mode 100644
index 0000000..a713a74
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/collections/event/PostsCollectionRemovedEventFacetAnnotationTest_newEvent.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.isis.core.progmodel.facets.collections.event;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Set;
+
+import org.junit.Test;
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+
+public class PostsCollectionRemovedEventFacetAnnotationTest_newEvent {
+
+    public static class SomeDomainObject {
+        public Set<SomeReferencedObject> getReferences() { return null; }
+    }
+    public static class SomeReferencedObject {}
+    
+    public static class SomeDomainObjectCollectionRemovedFromEvent extends CollectionRemovedFromEvent<SomeDomainObject, SomeReferencedObject> {}
+    
+    @Test
+    public void test() throws Exception {
+        SomeDomainObject sdo = new SomeDomainObject();
+        SomeReferencedObject other = new SomeReferencedObject();
+        Identifier identifier = Identifier.propertyOrCollectionIdentifier(SomeDomainObject.class, "references");
+
+        final CollectionRemovedFromEvent<SomeDomainObject, SomeReferencedObject> ev = PostsCollectionRemovedFromEventFacetAnnotation.newEvent(
+                SomeDomainObjectCollectionRemovedFromEvent.class, sdo, identifier, other);
+        assertThat(ev.getSource(), is(sdo));
+        assertThat(ev.getIdentifier(), is(identifier));
+        assertThat(ev.getValue(), is(other));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
index 5f9fc1d..6f81049 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java
@@ -488,6 +488,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     private SortedSet<ToDoItem> dependencies = new TreeSet<ToDoItem>();
 
     @PostsCollectionAddedToEvent(wrapperPolicy=WrapperPolicy.SKIP_RULES)
+    @PostsCollectionRemovedFromEvent(wrapperPolicy=WrapperPolicy.SKIP_RULES)
     @SortedBy(DependenciesComparator.class)
     public SortedSet<ToDoItem> getDependencies() {
         return dependencies;
@@ -508,6 +509,9 @@ public class ToDoItem implements Comparable<ToDoItem> {
     public ToDoItem add(
             @TypicalLength(20)
             final ToDoItem toDoItem) {
+    	// By wrapping the call, Isis will detect that the collection is modified 
+    	// and it will automatically send a CollectionAddedToEvent to the Event Bus.
+    	// See ToDoItemSubscriptions.
         wrapperFactory.wrap(this).addToDependencies(toDoItem);
         return this;
     }
@@ -538,7 +542,10 @@ public class ToDoItem implements Comparable<ToDoItem> {
     public ToDoItem remove(
             @TypicalLength(20)
             final ToDoItem toDoItem) {
-        this.removeFromDependencies(toDoItem);
+    	// By wrapping the call, Isis will detect that the collection is modified 
+    	// and it will automatically send a CollectionRemovedFromEvent to the Event Bus.
+    	// See ToDoItemSubscriptions.
+        wrapperFactory.wrap(this).removeFromDependencies(toDoItem);
         return this;
     }
     // disable action dependent on state of object

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
index 0a4b02f..15623bb 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
@@ -23,6 +23,7 @@ import com.google.common.eventbus.Subscribe;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent;
+import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
 import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
 
@@ -50,6 +51,12 @@ public class ToDoItemSubscriptions {
         LOG.info(container.titleOf(ev.getSource()) + ", added to " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
     }
     
+    @Programmatic
+    @Subscribe
+    public void on(CollectionRemovedFromEvent<?,?> ev) {
+        LOG.info(container.titleOf(ev.getSource()) + ", removed from " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
+    }
+    
     //region > injected services
     // //////////////////////////////////////
     

http://git-wip-us.apache.org/repos/asf/isis/blob/f8833040/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/events/ToDoItemTest_dependencies_addedEvent.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/events/ToDoItemTest_dependencies_addedEvent.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/events/ToDoItemTest_dependencies_addedEvent.java
new file mode 100644
index 0000000..6cfd491
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/tests/events/ToDoItemTest_dependencies_addedEvent.java
@@ -0,0 +1,85 @@
+/*
+ *  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 integration.tests.events;
+
+import integration.tests.ToDoIntegTest;
+
+import java.util.List;
+
+import dom.todo.ToDoItem;
+import dom.todo.ToDoItems;
+import fixture.todo.ToDoItemsFixture;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.eventbus.Subscribe;
+
+public class ToDoItemTest_dependencies_addedEvent extends ToDoIntegTest {
+
+    private ToDoItem toDoItem;
+    private ToDoItem otherToDoItem;
+    private EventBusService eventBusService;
+    
+    
+    public class TestSubscription {
+    	
+        @Programmatic
+        @Subscribe
+        public Boolean on(CollectionAddedToEvent<?,?> ev) {
+            return true;
+        }
+    	
+    }
+    
+
+    @Before
+    public void setUp() throws Exception {
+        scenarioExecution().install(new ToDoItemsFixture());
+
+        final List<ToDoItem> items = wrap(service(ToDoItems.class)).notYetComplete();
+        toDoItem = wrap(items.get(0));
+        otherToDoItem = items.get(1); // wrapping this seems to trip up cglib :-(
+        eventBusService = this.service(EventBusService.class);
+        
+        // Register new Service.
+        
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        unwrap(toDoItem).getDependencies().clear();
+    }
+
+    @Test
+    public void collectionAddedToEventReceived() throws Exception {
+
+        // given
+        
+        // when
+        toDoItem.add(otherToDoItem);
+        
+        // then
+    }
+
+}
\ No newline at end of file