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 2013/07/01 13:21:59 UTC
git commit: ISIS-452: @PostsPropertyChangedEvent annotation
Updated Branches:
refs/heads/master e1fa54a20 -> bd0be0e07
ISIS-452: @PostsPropertyChangedEvent annotation
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/bd0be0e0
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/bd0be0e0
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/bd0be0e0
Branch: refs/heads/master
Commit: bd0be0e078afe8aa2f1cd9290ae2c30aa87df938
Parents: e1fa54a
Author: Dan Haywood <da...@apache.org>
Authored: Mon Jul 1 12:21:44 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Mon Jul 1 12:21:44 2013 +0100
----------------------------------------------------------------------
.../service/eventbus/EventBusServiceJdo.java | 37 ++---
.../annotation/PostsPropertyChangedEvent.java | 59 +++++++
.../applib/services/eventbus/ChangedEvent.java | 49 ------
.../services/eventbus/EventBusService.java | 41 ++---
.../services/eventbus/PropertyChangedEvent.java | 61 +++++++
.../publish/PublishedActionFacetAbstract.java | 4 +-
.../event/PostsPropertyChangedEventFacet.java | 35 ++++
.../PostsPropertyChangedEventFacetAbstract.java | 37 +++++
...pertyChangedEventAnnotationFacetFactory.java | 117 +++++++++++++
...ostsPropertyChangedEventFacetAnnotation.java | 164 +++++++++++++++++++
.../dflt/ProgrammingModelFacetsJava5.java | 3 +
...hangedEventFacetAnnotationTest_newEvent.java | 42 +++++
.../eventbus/EventBusServiceDefault.java | 67 ++++++++
.../system/persistence/PersistenceSession.java | 16 ++
14 files changed, 628 insertions(+), 104 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/eventbus/EventBusServiceJdo.java
----------------------------------------------------------------------
diff --git a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/eventbus/EventBusServiceJdo.java b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/eventbus/EventBusServiceJdo.java
index 510f7d2..d13ecb3 100644
--- a/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/eventbus/EventBusServiceJdo.java
+++ b/component/objectstore/jdo/jdo-datanucleus/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/service/eventbus/EventBusServiceJdo.java
@@ -16,45 +16,30 @@
*/
package org.apache.isis.objectstore.jdo.datanucleus.service.eventbus;
-import java.util.Collection;
-
-import com.google.common.eventbus.EventBus;
-
-import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.objectstore.jdo.applib.service.support.IsisJdoSupport;
+import org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault;
import org.apache.isis.objectstore.jdo.datanucleus.JDOStateManagerForIsis;
import org.apache.isis.objectstore.jdo.datanucleus.JDOStateManagerForIsis.Hint;
-public class EventBusServiceJdo extends EventBusService {
-
-
- @Override
- protected EventBus getEventBus() {
- return IsisContext.getSession().getEventBus();
- }
-
- // //////////////////////////////////////
+/**
+ * An implementation that allows events to be {@link #post(Object) posted} from the
+ * setters of entities, automatically ignoring any calls to those setters that occur
+ * as a side-effect of the JDO load/detach lifecycle.
+ */
+public class EventBusServiceJdo extends EventBusServiceDefault {
- @Override
- protected void ensureLoaded(final Collection<?> collection) {
- isisJdoSupport.ensureLoaded(collection);
- }
/**
* skip if called in any way by way of the {@link JDOStateManagerForIsis}.
+ *
+ * <p>
+ * The {@link JDOStateManagerForIsis} sets a {@link JDOStateManagerForIsis#hint threadlocal}
+ * if it has been called.
*/
@Override
protected boolean skip(Object event) {
return JDOStateManagerForIsis.hint.get() != Hint.NONE;
}
- // //////////////////////////////////////
-
- private IsisJdoSupport isisJdoSupport;
- public void setIsisJdoSupport(IsisJdoSupport isisJdoSupport) {
- this.isisJdoSupport = isisJdoSupport;
- }
}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/applib/src/main/java/org/apache/isis/applib/annotation/PostsPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/PostsPropertyChangedEvent.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/PostsPropertyChangedEvent.java
new file mode 100644
index 0000000..f1d9491
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/PostsPropertyChangedEvent.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.applib.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+
+/**
+ * Applies only to properties; any changes should be propagated as events to subscribers.
+ * Only posted after a successful validation.
+ *
+ * <p>For example:
+ * <pre>
+ * public static class StartDateChangedEvent extends PropertyChangedEvent {}
+ *
+ * @PostsPropertyChangedEvent(StartDateChangedEvent.class)
+ * public LocalDate getStartDate() { ...}
+ * </pre>
+ *
+ * <p>
+ * It is highly advisable that only domain services - not domain entities - are registered as subscribers.
+ * Domain services are guaranteed to be instantiated and resident in memory, whereas the same is not true
+ * of domain entities. The typical implementation of a domain service subscriber is to identify the impacted entities,
+ * load them using a repository, and then to delegate to the event to them.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface PostsPropertyChangedEvent {
+
+ /**
+ * The subclass of {@link PropertyChangedEvent event} to be instantiated and posted.
+ *
+ * <p>
+ * This subclass must provide a no-arg constructor; the fields are set reflectively.
+ */
+ Class<? extends PropertyChangedEvent<?,?>> value();
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ChangedEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ChangedEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ChangedEvent.java
deleted file mode 100644
index d023d15..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.isis.applib.services.eventbus;
-
-import org.apache.isis.applib.util.ObjectContracts;
-
-public abstract class ChangedEvent<S,T> {
- private final S source;
- private final T oldValue;
- private final T newValue;
-
- public ChangedEvent(S source, T oldValue, T newValue) {
- this.source = source;
- this.oldValue = oldValue;
- this.newValue = newValue;
- }
-
- public S getSource() {
- return source;
- }
-
- public T getOldValue() {
- return oldValue;
- }
- public T getNewValue() {
- return newValue;
- }
-
- @Override
- public String toString() {
- return ObjectContracts.toString(this, "source,oldValue,newValue");
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
index 3414a22..53add32 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
@@ -16,17 +16,20 @@
*/
package org.apache.isis.applib.services.eventbus;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.PostConstruct;
-
-import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
import org.apache.isis.applib.annotation.Programmatic;
+/**
+ * A wrapper for a Guava {@link EventBus}, allowing arbitrary events to be posted and
+ * subscribed to.
+ *
+ * <p>
+ * It is highly advisable that only domain services - not domain entities - are registered as subscribers.
+ * Domain services are guaranteed to be instantiated and resident in memory, whereas the same is not true
+ * of domain entities. The typical implementation of a domain service subscriber is to identify the impacted entities,
+ * load them using a repository, and then to delegate to the event to them.
+ */
public abstract class EventBusService {
/**
@@ -39,7 +42,7 @@ public abstract class EventBusService {
@Override
public void unregister(Object domainObject) {};
@Override
- public void post(Object event, java.util.Collection<?>... collections) {}
+ public void post(Object event) {}
@Override
protected EventBus getEventBus() {
return null;
@@ -79,22 +82,17 @@ public abstract class EventBusService {
}
/**
- * Post an event, but ensuring that any possible subscribers
- * to that event have been brought into memory.
+ * Post an event.
*/
@Programmatic
- public void post(Object event, Collection<?>... collections ) {
+ public void post(Object event) {
if(skip(event)) {
return;
}
- final List<Object> list = Lists.newArrayList();
- for (Collection<?> collection : collections) {
- list.addAll(collection);
- }
- ensureLoaded(list);
getEventBus().post(event);
}
+
/**
* A hook to allow subclass implementations to skip the publication of certain events.
*
@@ -105,16 +103,5 @@ public abstract class EventBusService {
protected boolean skip(Object event) {
return false;
}
-
- /**
- * Overrideable hook method.
- *
- * <p>
- * If using JDO objectstore, then use the <tt>EventBusServiceJdo</tt> implementation,
- * which overrides this method to load objects from the database.
- */
- protected void ensureLoaded(final Collection<?> collection) {
- }
-
}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/PropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/PropertyChangedEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/PropertyChangedEvent.java
new file mode 100644
index 0000000..e528867
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/PropertyChangedEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.applib.services.eventbus;
+
+import org.apache.isis.applib.annotation.PostsPropertyChangedEvent;
+import org.apache.isis.applib.util.ObjectContracts;
+
+public abstract class PropertyChangedEvent<S,T> {
+ private final S source;
+ private final T oldValue;
+ private final T newValue;
+
+ /**
+ * To instantiate reflectively when the {@link PostsPropertyChangedEvent} annotation
+ * is used.
+ *
+ * <p>
+ * The fields ({@link #source}, {@link #oldValue} and {@link #newValue}) are
+ * then set reflectively.
+ */
+ public PropertyChangedEvent() {
+ this(null, null, null);
+ }
+ public PropertyChangedEvent(S source, T oldValue, T newValue) {
+ this.source = source;
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ }
+
+ public S getSource() {
+ return source;
+ }
+
+ public T getOldValue() {
+ return oldValue;
+ }
+ public T getNewValue() {
+ return newValue;
+ }
+
+ @Override
+ public String toString() {
+ return ObjectContracts.toString(this, "source,oldValue,newValue");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/publish/PublishedActionFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/publish/PublishedActionFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/publish/PublishedActionFacetAbstract.java
index 3923bcf..e964111 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/publish/PublishedActionFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/publish/PublishedActionFacetAbstract.java
@@ -30,8 +30,8 @@ public abstract class PublishedActionFacetAbstract extends SingleValueFacetAbstr
return PublishedActionFacet.class;
}
- public PublishedActionFacetAbstract(final PublishedAction.PayloadFactory eventCanonicalizer, final FacetHolder holder) {
- super(type(), eventCanonicalizer, holder);
+ public PublishedActionFacetAbstract(final PublishedAction.PayloadFactory payloadFactory, final FacetHolder holder) {
+ super(type(), payloadFactory, holder);
}
}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacet.java
new file mode 100644
index 0000000..a01e7f4
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacet.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.properties.event;
+
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+import org.apache.isis.core.metamodel.facetapi.MultiTypedFacet;
+import org.apache.isis.core.metamodel.facets.SingleValueFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet;
+
+/**
+ * Indicates that (the specified subclass of) {@link PropertyChangedEvent} should be posted to the
+ * {@link EventBusService}.
+ */
+public interface PostsPropertyChangedEventFacet extends SingleValueFacet<Class<? extends PropertyChangedEvent<?,?>>>, PropertyClearFacet, PropertySetterFacet, MultiTypedFacet {
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacetAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacetAbstract.java
new file mode 100644
index 0000000..281438a
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/event/PostsPropertyChangedEventFacetAbstract.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.properties.event;
+
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.SingleValueFacetAbstract;
+
+public abstract class PostsPropertyChangedEventFacetAbstract extends SingleValueFacetAbstract<Class<? extends PropertyChangedEvent<?,?>>> implements PostsPropertyChangedEventFacet {
+
+ public static Class<? extends Facet> type() {
+ return PostsPropertyChangedEventFacet.class;
+ }
+
+ public PostsPropertyChangedEventFacetAbstract(final Class<? extends PropertyChangedEvent<?,?>> changedEventType, final FacetHolder holder) {
+ super(type(), changedEventType, holder);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventAnnotationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventAnnotationFacetFactory.java
new file mode 100644
index 0000000..48d12d0
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventAnnotationFacetFactory.java
@@ -0,0 +1,117 @@
+/*
+ * 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.properties.event;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.isis.applib.annotation.PostsPropertyChangedEvent;
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+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.facetapi.MetaModelValidatorRefiner;
+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.sortedby.SortedByFacet;
+import org.apache.isis.core.metamodel.facets.properties.event.PostsPropertyChangedEventFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting.Visitor;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+
+public class PostsPropertyChangedEventAnnotationFacetFactory extends FacetFactoryAbstract implements ServicesProviderAware, MetaModelValidatorRefiner {
+
+ private ServicesProvider servicesProvider;
+
+ public PostsPropertyChangedEventAnnotationFacetFactory() {
+ super(FeatureType.PROPERTIES_ONLY);
+ }
+
+ @Override
+ public void process(final ProcessMethodContext processMethodContext) {
+ final Method method = processMethodContext.getMethod();
+ FacetUtil.addFacet(create(method, processMethodContext.getFacetHolder()));
+ }
+
+ private PostsPropertyChangedEventFacet create(Method method, final FacetHolder holder) {
+ final PostsPropertyChangedEvent annotation = Annotations.getAnnotation(method, PostsPropertyChangedEvent.class);
+ if(annotation == null) {
+ return null;
+ }
+ final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class);
+ if(getterFacet == null) {
+ return null;
+ }
+ final PropertyClearFacet clearFacet = holder.getFacet(PropertyClearFacet.class);
+ final PropertySetterFacet setterFacet = holder.getFacet(PropertySetterFacet.class);
+ if (clearFacet == null && setterFacet == null) {
+ return null;
+ }
+ if(setterFacet != null) {
+ holder.removeFacet(setterFacet);
+ }
+ if(clearFacet != null) {
+ holder.removeFacet(clearFacet);
+ }
+ final Class<? extends PropertyChangedEvent<?, ?>> changedEventType = annotation.value();
+ return new PostsPropertyChangedEventFacetAnnotation(changedEventType, getterFacet, setterFacet, clearFacet, servicesProvider, holder);
+ }
+
+ @Override
+ public void setServicesProvider(ServicesProvider servicesProvider) {
+ this.servicesProvider = servicesProvider;
+ }
+
+ @Override
+ public void refineMetaModelValidator(MetaModelValidatorComposite metaModelValidator, IsisConfiguration configuration) {
+ metaModelValidator.add(new MetaModelValidatorVisiting(newValidatorVisitor()));
+ }
+
+ protected Visitor newValidatorVisitor() {
+ return new MetaModelValidatorVisiting.Visitor() {
+
+ @Override
+ public boolean visit(ObjectSpecification objectSpec, ValidationFailures validationFailures) {
+ List<OneToManyAssociation> objectCollections = objectSpec.getCollections();
+ for (OneToManyAssociation objectCollection : objectCollections) {
+ final SortedByFacet facet = objectCollection.getFacet(SortedByFacet.class);
+ if(facet != null) {
+ final Class<? extends Comparator<?>> cls = facet.value();
+ if(!Comparator.class.isAssignableFrom(cls)) {
+ validationFailures.add("%s#%s is annotated with @SortedBy, but the class specified '%s' is not a Comparator", objectSpec.getIdentifier().getClassName(), objectCollection.getId(), facet.value().getName());
+ }
+ }
+ }
+ return true;
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
new file mode 100644
index 0000000..57d5ff3
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotation.java
@@ -0,0 +1,164 @@
+/*
+ * 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.properties.event;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+
+import org.apache.isis.applib.ApplicationException;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ServicesProvider;
+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.properties.event.PostsPropertyChangedEventFacet;
+import org.apache.isis.core.metamodel.facets.properties.event.PostsPropertyChangedEventFacetAbstract;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet;
+
+public class PostsPropertyChangedEventFacetAnnotation extends PostsPropertyChangedEventFacetAbstract {
+
+ private final PropertyOrCollectionAccessorFacet getterFacet;
+ private final PropertySetterFacet setterFacet;
+ private final PropertyClearFacet clearFacet;
+ private final ServicesProvider servicesProvider;
+
+ private EventBusService eventBusService;
+ private boolean searchedForEventBusService = false;
+
+ public PostsPropertyChangedEventFacetAnnotation(
+ final Class<? extends PropertyChangedEvent<?, ?>> changedEventType,
+ final PropertyOrCollectionAccessorFacet getterFacet,
+ final PropertySetterFacet setterFacet,
+ final PropertyClearFacet clearFacet,
+ final ServicesProvider servicesProvider,
+ final FacetHolder holder) {
+ super(changedEventType, holder);
+ this.getterFacet = getterFacet;
+ this.setterFacet = setterFacet;
+ this.clearFacet = clearFacet;
+ this.servicesProvider = servicesProvider;
+ }
+
+ @Override
+ public void setProperty(ObjectAdapter inObject, ObjectAdapter value) {
+ if(this.setterFacet == null) {
+ return;
+ }
+ eventBusService = getEventBusService();
+ if(eventBusService == null) {
+ setterFacet.setProperty(inObject, value);
+ return;
+ }
+
+ final Object oldValue = this.getterFacet.getProperty(inObject);
+ this.setterFacet.setProperty(inObject, value);
+ final Object newValue = this.getterFacet.getProperty(inObject);
+ postEventIfChanged(inObject, oldValue, newValue);
+ }
+
+ @Override
+ public void clearProperty(ObjectAdapter inObject) {
+ if(this.clearFacet == null) {
+ return;
+ }
+ eventBusService = getEventBusService();
+ if(eventBusService == null) {
+ clearFacet.clearProperty(inObject);
+ return;
+ }
+
+ final Object oldValue = this.getterFacet.getProperty(inObject);
+ this.clearFacet.clearProperty(inObject);
+ final Object newValue = this.getterFacet.getProperty(inObject);
+ postEventIfChanged(inObject, oldValue, newValue);
+ }
+
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private void postEventIfChanged(ObjectAdapter inObject, final Object oldValue, final Object newValue) {
+ if(Objects.equal(oldValue, newValue)) {
+ // do nothing.
+ return;
+ }
+ final Object source = inObject.getObject();
+ try {
+ final Class type = value();
+ final PropertyChangedEvent<?, ?> event = newEvent(type, oldValue, newValue, source);
+
+ eventBusService.post(event);
+ } catch (Exception e) {
+ throw new ApplicationException(e);
+ }
+ }
+
+ static <S,T> PropertyChangedEvent<S,T> newEvent(final Class<? extends PropertyChangedEvent<S, T>> type, final T oldValue, final T newValue, final S source) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
+ final PropertyChangedEvent<S, T> event = type.newInstance();
+
+ setField("source", event, source);
+ setField("oldValue", event, oldValue);
+ setField("newValue", event, newValue);
+ return event;
+ }
+
+ private static void setField(final String name, final PropertyChangedEvent<?, ?> event, final Object sourceValue) throws NoSuchFieldException, IllegalAccessException {
+ final Field sourceField = PropertyChangedEvent.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(PostsPropertyChangedEventFacet.class, PropertySetterFacet.class, PropertyClearFacet.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/bd0be0e0/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 29bb10b..9150f59 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
@@ -151,6 +151,7 @@ import org.apache.isis.core.progmodel.facets.properties.choices.method.PropertyC
import org.apache.isis.core.progmodel.facets.properties.defaults.fromtype.PropertyDefaultDerivedFromTypeFacetFactory;
import org.apache.isis.core.progmodel.facets.properties.defaults.method.PropertyDefaultFacetFactory;
import org.apache.isis.core.progmodel.facets.properties.disabled.fromimmutable.DisabledFacetForPropertyDerivedFromImmutableTypeFacetFactory;
+import org.apache.isis.core.progmodel.facets.properties.event.PostsPropertyChangedEventAnnotationFacetFactory;
import org.apache.isis.core.progmodel.facets.properties.mandatory.annotation.OptionalAnnotationForPropertyFacetFactory;
import org.apache.isis.core.progmodel.facets.properties.mandatory.dflt.MandatoryDefaultForPropertiesFacetFactory;
import org.apache.isis.core.progmodel.facets.properties.mandatory.staticmethod.PropertyOptionalFacetFactory;
@@ -345,6 +346,8 @@ public final class ProgrammingModelFacetsJava5 extends ProgrammingModelAbstract
addFactory(DisabledFacetForPropertyDerivedFromImmutableTypeFacetFactory.class);
addFactory(DisabledFacetForCollectionDerivedFromImmutableTypeFacetFactory.class);
+ addFactory(PostsPropertyChangedEventAnnotationFacetFactory.class);
+
addFactory(ImmutableMarkerInterfaceFacetFactory.class);
addFactory(ViewModelAnnotationFacetFactory.class);
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotationTest_newEvent.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotationTest_newEvent.java b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotationTest_newEvent.java
new file mode 100644
index 0000000..eeddb35
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/progmodel/facets/properties/event/PostsPropertyChangedEventFacetAnnotationTest_newEvent.java
@@ -0,0 +1,42 @@
+/**
+ * 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.properties.event;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import org.joda.time.LocalDate;
+import org.junit.Test;
+
+import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+
+public class PostsPropertyChangedEventFacetAnnotationTest_newEvent {
+
+ public static class SomeDomainObject {}
+
+ public static class SomeDatePropertyChangedEvent extends PropertyChangedEvent<SomeDomainObject, LocalDate> {}
+
+ @Test
+ public void test() throws Exception {
+ SomeDomainObject sdo = new SomeDomainObject();
+ final PropertyChangedEvent<SomeDomainObject, LocalDate> ev = PostsPropertyChangedEventFacetAnnotation.newEvent(SomeDatePropertyChangedEvent.class, new LocalDate(2013,4,1), new LocalDate(2013,5,2), sdo);
+ assertThat(ev.getSource(), is(sdo));
+ assertThat(ev.getOldValue(), is(new LocalDate(2013,4,1)));
+ assertThat(ev.getNewValue(), is(new LocalDate(2013,5,2)));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
new file mode 100644
index 0000000..f689a55
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.isis.core.runtime.services.eventbus;
+
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+import com.google.common.eventbus.EventBus;
+
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+
+public class EventBusServiceDefault extends EventBusService {
+
+
+ private final Set<Object> objectsToRegister = Sets.newHashSet();
+
+ @Override
+ protected EventBus getEventBus() {
+ return IsisContext.getSession().getEventBus();
+ }
+
+
+ @Override
+ public void register(Object domainObject) {
+ // lazily registered
+ // (a) there may be no session initially
+ // (b) so can be unregistered at when closed
+ objectsToRegister.add(domainObject);
+ }
+
+ @Override
+ public void unregister(Object domainObject) {
+ if(IsisContext.inSession()) {
+ getEventBus().unregister(domainObject);
+ }
+ objectsToRegister.remove(domainObject);
+ }
+
+ public void open() {
+ for (final Object object : objectsToRegister) {
+ getEventBus().register(object);
+ }
+ }
+
+ public void close() {
+ for (final Object object : objectsToRegister) {
+ getEventBus().unregister(object);
+ }
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/isis/blob/bd0be0e0/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
index 9b39e3c..48f980d 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
@@ -83,6 +83,7 @@ import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindByPatt
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindByTitle;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindUsingApplibQueryDefault;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryFindUsingApplibQuerySerializable;
+import org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.core.runtime.system.transaction.EnlistedObjectDirtying;
import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
@@ -238,6 +239,11 @@ public class PersistenceSession implements Persistor, EnlistedObjectDirtying, To
if (LOG.isDebugEnabled()) {
LOG.debug("closing " + this);
}
+
+ // a bit of a hack
+ if(eventBusService != null) {
+ eventBusService.close();
+ }
try {
objectStore.close();
@@ -286,6 +292,14 @@ public class PersistenceSession implements Persistor, EnlistedObjectDirtying, To
final RootOid persistentOid = (RootOid) serviceAdapter.getOid();
registerService(persistentOid);
}
+
+ // a bit of a hack
+ final Object object = serviceAdapter.getObject();
+ if(object instanceof EventBusServiceDefault) {
+ eventBusService = (EventBusServiceDefault) object;
+ EventBusServiceDefault ebs = eventBusService;
+ ebs.open();
+ }
}
getTransactionManager().endTransaction();
@@ -945,6 +959,8 @@ public class PersistenceSession implements Persistor, EnlistedObjectDirtying, To
private Map<Oid, Oid> persistentByTransient = Maps.newHashMap();
+ private EventBusServiceDefault eventBusService;
+
/**
* Callback from the {@link PersistAlgorithm} (or equivalent; some object
* stores such as Hibernate will use listeners instead) to indicate that the