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/02 19:22:32 UTC

[4/4] git commit: ISIS-772: reimplemented.

ISIS-772: reimplemented.

... so that WrapperFactoryDefault now uses javassist.  WrapperFactoryCglib is the original impl.  To use, explicitly include cglib-nodep and asm in pom.xml (they are now marked as optional).

In addition:
- new tests in todo app to demonstrate some of the usage of this
- fixed an error that the use of 'modify' in wrapped methods was not being disallowed
- resurrected previously-@Ignore'd unit tests for wrapper factory.


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

Branch: refs/heads/master
Commit: 074d2c4e45f90e27e903ab0823c109eacbdefe71
Parents: 72a1911
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Fri May 2 18:22:19 2014 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Fri May 2 18:22:19 2014 +0100

----------------------------------------------------------------------
 core/unittestsupport/pom.xml                    |   1 -
 .../jmocking/JavassistImposteriser.java         |  14 +-
 core/wrapper/pom.xml                            |  31 +-
 .../core/wrapper/WrapperFactoryAbstract.java    | 315 +++++++++
 .../isis/core/wrapper/WrapperFactoryCglib.java  |  30 +
 .../core/wrapper/WrapperFactoryDefault.java     | 269 +-------
 .../core/wrapper/WrapperFactoryJavassist.java   |  30 +
 .../dispatchers/InteractionEventDispatcher.java |  28 +
 .../InteractionEventDispatcherTypeSafe.java     |  34 +
 .../AbstractCollectionInvocationHandler.java    |  89 +++
 .../handlers/CollectionInvocationHandler.java   |  53 ++
 .../handlers/DelegatingInvocationHandler.java   |  32 +
 .../DelegatingInvocationHandlerDefault.java     | 131 ++++
 .../handlers/DomainObjectInvocationHandler.java | 679 +++++++++++++++++++
 .../wrapper/handlers/MapInvocationHandler.java  |  48 ++
 .../wrapper/handlers/ProxyContextHandler.java   |  84 +++
 .../AbstractCollectionInvocationHandler.java    |  89 ---
 .../internal/CgLibClassProxyFactory.java        |  71 --
 .../isis/core/wrapper/internal/CgLibProxy.java  |  74 --
 .../internal/ClassInstantiatorFactoryCE.java    |  65 --
 .../internal/CollectionInvocationHandler.java   |  53 --
 .../internal/DelegatingInvocationHandler.java   |  32 -
 .../DelegatingInvocationHandlerDefault.java     | 131 ----
 .../internal/DomainObjectInvocationHandler.java | 669 ------------------
 .../wrapper/internal/IClassInstantiatorCE.java  |  37 -
 .../core/wrapper/internal/IProxyFactory.java    |  28 -
 .../internal/InteractionEventDispatcher.java    |  28 -
 .../InteractionEventDispatcherTypeSafe.java     |  34 -
 .../InvocationHandlerMethodInterceptor.java     |  39 --
 .../core/wrapper/internal/JavaProxyFactory.java |  40 --
 .../wrapper/internal/MapInvocationHandler.java  |  48 --
 .../internal/ObjenesisClassInstantiatorCE.java  |  31 -
 .../isis/core/wrapper/internal/Proxy.java       |  80 ---
 .../internal/RuntimeExceptionWrapper.java       |  33 -
 .../isis/core/wrapper/internal/util/Util.java   |  53 ++
 .../core/wrapper/proxy/ProxyInstantiator.java   |  26 +
 .../proxy/ProxyInstantiatorForCglib.java        | 101 +++
 .../proxy/ProxyInstantiatorForJavassist.java    |  77 +++
 ...WrappedFactoryDefaultTest_wrappedObject.java | 153 -----
 ...toryDefaultTest_wrappedObject_transient.java | 251 -------
 ...rapperFactoryAbstractTest_wrappedObject.java | 380 +++++++++++
 ...oryAbstractTest_wrappedObject_transient.java | 257 +++++++
 .../WrapperFactoryCglibTest_wrappedObject.java  |  32 +
 ...actoryCglibTest_wrappedObject_transient.java |  73 ++
 ...apperFactoryJavassistTest_wrappedObject.java |  32 +
 ...ryJavassistTest_wrappedObject_transient.java |  31 +
 .../dom/src/main/java/dom/todo/ToDoItem.java    |   7 +
 .../tests/props/ToDoItemTest_description.java   |  31 +
 48 files changed, 2678 insertions(+), 2276 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/unittestsupport/pom.xml
----------------------------------------------------------------------
diff --git a/core/unittestsupport/pom.xml b/core/unittestsupport/pom.xml
index fe0fc1c..fe70990 100644
--- a/core/unittestsupport/pom.xml
+++ b/core/unittestsupport/pom.xml
@@ -97,7 +97,6 @@
             <dependency>
                 <groupId>org.javassist</groupId>
                 <artifactId>javassist</artifactId>
-                <!--<version>3.18.1-GA</version>-->
             </dependency>
 
             <dependency>

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/jmocking/JavassistImposteriser.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/jmocking/JavassistImposteriser.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/jmocking/JavassistImposteriser.java
index 0fc9332..df56eb5 100644
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/jmocking/JavassistImposteriser.java
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/jmocking/JavassistImposteriser.java
@@ -97,8 +97,7 @@ public class JavassistImposteriser implements Imposteriser {
 
     private Class<?> proxyClass(Class<?> mockedType, Class<?>... ancilliaryTypes) {
 
-        final ProxyFactory proxyFactory;
-        proxyFactory = new ProxyFactory();
+        final ProxyFactory proxyFactory = new ProxyFactory();
         proxyFactory.setFilter(new MethodFilter() {
             @Override
             public boolean isHandled(final Method m) {
@@ -107,13 +106,8 @@ public class JavassistImposteriser implements Imposteriser {
             }
         });
 
-        if(mockedType.isInterface()) {
-            proxyFactory.setSuperclass(Object.class);
-            proxyFactory.setInterfaces(prepend(mockedType, ancilliaryTypes));
-        } else {
-            proxyFactory.setSuperclass(mockedType);
-            proxyFactory.setInterfaces(ancilliaryTypes);
-        }
+        proxyFactory.setSuperclass(mockedType);
+        proxyFactory.setInterfaces(ancilliaryTypes);
 
         return proxyFactory.createClass();
 
@@ -197,7 +191,7 @@ public class JavassistImposteriser implements Imposteriser {
         //        return proxy;
     }
 
-    private Class<?>[] prepend(Class<?> first, Class<?>... rest) {
+    private static Class<?>[] combine(Class<?> first, Class<?>... rest) {
         Class<?>[] all = new Class<?>[rest.length+1];
         all[0] = first;
         System.arraycopy(rest, 0, all, 1, rest.length);

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/pom.xml
----------------------------------------------------------------------
diff --git a/core/wrapper/pom.xml b/core/wrapper/pom.xml
index 9836543..ee90ddf 100644
--- a/core/wrapper/pom.xml
+++ b/core/wrapper/pom.xml
@@ -109,20 +109,29 @@
             <scope>test</scope>
         </dependency>
 
-		<dependency>
+        <dependency>
+            <groupId>org.objenesis</groupId>
+            <artifactId>objenesis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>asm</groupId>
             <artifactId>asm</artifactId>
-		</dependency>
-
-		<dependency>
-			<groupId>org.objenesis</groupId>
-			<artifactId>objenesis</artifactId>
-		</dependency>
+            <!-- explicitly include for use of WrapperFactoryCglib -->
+            <optional>true</optional>
+        </dependency>
 
-		<dependency>
-			<groupId>cglib</groupId>
-			<artifactId>cglib-nodep</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <!-- explicitly include for use of WrapperFactoryCglib -->
+            <optional>true</optional>
+        </dependency>
 
 	</dependencies>
 

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryAbstract.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryAbstract.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryAbstract.java
new file mode 100644
index 0000000..88c33c8
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryAbstract.java
@@ -0,0 +1,315 @@
+/*
+ *  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.wrapper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.events.ActionArgumentEvent;
+import org.apache.isis.applib.events.ActionInvocationEvent;
+import org.apache.isis.applib.events.ActionUsabilityEvent;
+import org.apache.isis.applib.events.ActionVisibilityEvent;
+import org.apache.isis.applib.events.CollectionAccessEvent;
+import org.apache.isis.applib.events.CollectionAddToEvent;
+import org.apache.isis.applib.events.CollectionMethodEvent;
+import org.apache.isis.applib.events.CollectionRemoveFromEvent;
+import org.apache.isis.applib.events.CollectionUsabilityEvent;
+import org.apache.isis.applib.events.CollectionVisibilityEvent;
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.applib.events.ObjectTitleEvent;
+import org.apache.isis.applib.events.ObjectValidityEvent;
+import org.apache.isis.applib.events.PropertyAccessEvent;
+import org.apache.isis.applib.events.PropertyModifyEvent;
+import org.apache.isis.applib.events.PropertyUsabilityEvent;
+import org.apache.isis.applib.events.PropertyVisibilityEvent;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.WrapperObject;
+import org.apache.isis.applib.services.wrapper.listeners.InteractionListener;
+import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
+import org.apache.isis.core.commons.authentication.AuthenticationSessionProviderAware;
+import org.apache.isis.core.metamodel.adapter.ObjectPersistor;
+import org.apache.isis.core.metamodel.adapter.ObjectPersistorAware;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
+import org.apache.isis.core.metamodel.spec.SpecificationLoader;
+import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware;
+import org.apache.isis.core.wrapper.dispatchers.InteractionEventDispatcher;
+import org.apache.isis.core.wrapper.dispatchers.InteractionEventDispatcherTypeSafe;
+import org.apache.isis.core.wrapper.handlers.ProxyContextHandler;
+import org.apache.isis.core.wrapper.proxy.ProxyInstantiator;
+
+public abstract class WrapperFactoryAbstract implements WrapperFactory, AuthenticationSessionProviderAware, SpecificationLoaderAware, AdapterManagerAware, ObjectPersistorAware {
+
+    private final List<InteractionListener> listeners = new ArrayList<InteractionListener>();
+    private final Map<Class<? extends InteractionEvent>, InteractionEventDispatcher> dispatchersByEventClass = new HashMap<Class<? extends InteractionEvent>, InteractionEventDispatcher>();
+
+    private AuthenticationSessionProvider authenticationSessionProvider;
+    private SpecificationLoader specificationLookup;
+    private AdapterManager adapterManager;
+    private ObjectPersistor objectPersistor;
+
+    private final ProxyContextHandler proxy;
+
+    public WrapperFactoryAbstract(final ProxyInstantiator proxyInstantiator) {
+        
+        proxy = new ProxyContextHandler(proxyInstantiator);
+        
+        dispatchersByEventClass.put(ObjectTitleEvent.class, new InteractionEventDispatcherTypeSafe<ObjectTitleEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ObjectTitleEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.objectTitleRead(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(PropertyVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<PropertyVisibilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final PropertyVisibilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.propertyVisible(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(PropertyUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<PropertyUsabilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final PropertyUsabilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.propertyUsable(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(PropertyAccessEvent.class, new InteractionEventDispatcherTypeSafe<PropertyAccessEvent>() {
+            @Override
+            public void dispatchTypeSafe(final PropertyAccessEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.propertyAccessed(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(PropertyModifyEvent.class, new InteractionEventDispatcherTypeSafe<PropertyModifyEvent>() {
+            @Override
+            public void dispatchTypeSafe(final PropertyModifyEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.propertyModified(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<CollectionVisibilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionVisibilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionVisible(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<CollectionUsabilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionUsabilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionUsable(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionAccessEvent.class, new InteractionEventDispatcherTypeSafe<CollectionAccessEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionAccessEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionAccessed(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionAddToEvent.class, new InteractionEventDispatcherTypeSafe<CollectionAddToEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionAddToEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionAddedTo(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionRemoveFromEvent.class, new InteractionEventDispatcherTypeSafe<CollectionRemoveFromEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionRemoveFromEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionRemovedFrom(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(ActionVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<ActionVisibilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ActionVisibilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.actionVisible(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(ActionUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<ActionUsabilityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ActionUsabilityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.actionUsable(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(ActionArgumentEvent.class, new InteractionEventDispatcherTypeSafe<ActionArgumentEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ActionArgumentEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.actionArgument(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(ActionInvocationEvent.class, new InteractionEventDispatcherTypeSafe<ActionInvocationEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ActionInvocationEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.actionInvoked(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(ObjectValidityEvent.class, new InteractionEventDispatcherTypeSafe<ObjectValidityEvent>() {
+            @Override
+            public void dispatchTypeSafe(final ObjectValidityEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.objectPersisted(interactionEvent);
+                }
+            }
+        });
+        dispatchersByEventClass.put(CollectionMethodEvent.class, new InteractionEventDispatcherTypeSafe<CollectionMethodEvent>() {
+            @Override
+            public void dispatchTypeSafe(final CollectionMethodEvent interactionEvent) {
+                for (final InteractionListener l : getListeners()) {
+                    l.collectionMethodInvoked(interactionEvent);
+                }
+            }
+        });
+    }
+
+    // /////////////////////////////////////////////////////////////
+    // wrap and unwrap
+    // /////////////////////////////////////////////////////////////
+
+    @Override
+    public <T> T wrap(final T domainObject) {
+        return wrap(domainObject, ExecutionMode.EXECUTE);
+    }
+
+    @Override
+    public <T> T wrap(final T domainObject, final ExecutionMode mode) {
+        if (isWrapper(domainObject)) {
+            return domainObject;
+        }
+        return createProxy(domainObject, mode);
+    }
+
+    protected <T> T createProxy(final T domainObject, final ExecutionMode mode) {
+        return proxy.proxy(domainObject, this, mode, getAuthenticationSessionProvider(), getSpecificationLookup(), getAdapterManager(), getObjectPersistor());
+    }
+
+    @Override
+    public boolean isWrapper(final Object possibleWrappedDomainObject) {
+        return possibleWrappedDomainObject instanceof WrapperObject;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    @Programmatic
+    public <T> T unwrap(T possibleWrappedDomainObject) {
+        if(isWrapper(possibleWrappedDomainObject)) {
+            WrapperObject wrapperObject = (WrapperObject) possibleWrappedDomainObject;
+            return (T) wrapperObject.wrapped();
+        } 
+        return possibleWrappedDomainObject;
+    }
+
+    // /////////////////////////////////////////////////////////////
+    // Listeners
+    // /////////////////////////////////////////////////////////////
+
+    @Override
+    public List<InteractionListener> getListeners() {
+        return listeners;
+    }
+
+    @Override
+    public boolean addInteractionListener(final InteractionListener listener) {
+        return listeners.add(listener);
+    }
+
+    @Override
+    public boolean removeInteractionListener(final InteractionListener listener) {
+        return listeners.remove(listener);
+    }
+
+    @Override
+    public void notifyListeners(final InteractionEvent interactionEvent) {
+        final InteractionEventDispatcher dispatcher = dispatchersByEventClass.get(interactionEvent.getClass());
+        if (dispatcher == null) {
+            throw new RuntimeException("Unknown InteractionEvent - register into dispatchers map");
+        }
+        dispatcher.dispatch(interactionEvent);
+    }
+
+    // /////////////////////////////////////////////////////////////
+    // Listeners
+    // /////////////////////////////////////////////////////////////
+
+    protected AuthenticationSessionProvider getAuthenticationSessionProvider() {
+        return authenticationSessionProvider;
+    }
+    @Programmatic
+    @Override
+    public void setAuthenticationSessionProvider(final AuthenticationSessionProvider authenticationSessionProvider) {
+        this.authenticationSessionProvider = authenticationSessionProvider;
+    }
+
+    protected AdapterManager getAdapterManager() {
+        return adapterManager;
+    }
+    @Programmatic
+    @Override
+    public void setAdapterManager(final AdapterManager adapterManager) {
+        this.adapterManager = adapterManager;
+    }
+
+    
+    protected SpecificationLoader getSpecificationLookup() {
+        return specificationLookup;
+    }
+    @Programmatic
+    @Override
+    public void setSpecificationLookup(final SpecificationLoader specificationLookup) {
+        this.specificationLookup = specificationLookup;
+    }
+
+    protected ObjectPersistor getObjectPersistor() {
+        return objectPersistor;
+    }
+    @Programmatic
+    @Override
+    public void setObjectPersistor(final ObjectPersistor objectPersistor) {
+        this.objectPersistor = objectPersistor;
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryCglib.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryCglib.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryCglib.java
new file mode 100644
index 0000000..fd75b59
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryCglib.java
@@ -0,0 +1,30 @@
+/*
+ *  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.wrapper;
+
+import org.apache.isis.core.wrapper.proxy.ProxyInstantiatorForCglib;
+
+public class WrapperFactoryCglib extends WrapperFactoryAbstract  {
+
+    public WrapperFactoryCglib() {
+        super(new ProxyInstantiatorForCglib());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryDefault.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryDefault.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryDefault.java
index 1e89bc2..62bc688 100644
--- a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryDefault.java
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryDefault.java
@@ -19,274 +19,7 @@
 
 package org.apache.isis.core.wrapper;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.events.ActionArgumentEvent;
-import org.apache.isis.applib.events.ActionInvocationEvent;
-import org.apache.isis.applib.events.ActionUsabilityEvent;
-import org.apache.isis.applib.events.ActionVisibilityEvent;
-import org.apache.isis.applib.events.CollectionAccessEvent;
-import org.apache.isis.applib.events.CollectionAddToEvent;
-import org.apache.isis.applib.events.CollectionMethodEvent;
-import org.apache.isis.applib.events.CollectionRemoveFromEvent;
-import org.apache.isis.applib.events.CollectionUsabilityEvent;
-import org.apache.isis.applib.events.CollectionVisibilityEvent;
-import org.apache.isis.applib.events.InteractionEvent;
-import org.apache.isis.applib.events.ObjectTitleEvent;
-import org.apache.isis.applib.events.ObjectValidityEvent;
-import org.apache.isis.applib.events.PropertyAccessEvent;
-import org.apache.isis.applib.events.PropertyModifyEvent;
-import org.apache.isis.applib.events.PropertyUsabilityEvent;
-import org.apache.isis.applib.events.PropertyVisibilityEvent;
-import org.apache.isis.applib.services.wrapper.WrapperFactory;
-import org.apache.isis.applib.services.wrapper.WrapperObject;
-import org.apache.isis.applib.services.wrapper.listeners.InteractionListener;
-import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
-import org.apache.isis.core.commons.authentication.AuthenticationSessionProviderAware;
-import org.apache.isis.core.metamodel.adapter.ObjectPersistor;
-import org.apache.isis.core.metamodel.adapter.ObjectPersistorAware;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
-import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
-import org.apache.isis.core.metamodel.spec.SpecificationLoader;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware;
-import org.apache.isis.core.wrapper.internal.InteractionEventDispatcher;
-import org.apache.isis.core.wrapper.internal.InteractionEventDispatcherTypeSafe;
-import org.apache.isis.core.wrapper.internal.Proxy;
-
-public class WrapperFactoryDefault implements WrapperFactory, AuthenticationSessionProviderAware, SpecificationLoaderAware, AdapterManagerAware, ObjectPersistorAware {
-
-    private final List<InteractionListener> listeners = new ArrayList<InteractionListener>();
-    private final Map<Class<? extends InteractionEvent>, InteractionEventDispatcher> dispatchersByEventClass = new HashMap<Class<? extends InteractionEvent>, InteractionEventDispatcher>();
-
-    private AuthenticationSessionProvider authenticationSessionProvider;
-    private SpecificationLoader specificationLookup;
-    private AdapterManager adapterManager;
-    private ObjectPersistor objectPersistor;
-
-    public WrapperFactoryDefault() {
-        dispatchersByEventClass.put(ObjectTitleEvent.class, new InteractionEventDispatcherTypeSafe<ObjectTitleEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ObjectTitleEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.objectTitleRead(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(PropertyVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<PropertyVisibilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final PropertyVisibilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.propertyVisible(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(PropertyUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<PropertyUsabilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final PropertyUsabilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.propertyUsable(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(PropertyAccessEvent.class, new InteractionEventDispatcherTypeSafe<PropertyAccessEvent>() {
-            @Override
-            public void dispatchTypeSafe(final PropertyAccessEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.propertyAccessed(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(PropertyModifyEvent.class, new InteractionEventDispatcherTypeSafe<PropertyModifyEvent>() {
-            @Override
-            public void dispatchTypeSafe(final PropertyModifyEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.propertyModified(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<CollectionVisibilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionVisibilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionVisible(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<CollectionUsabilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionUsabilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionUsable(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionAccessEvent.class, new InteractionEventDispatcherTypeSafe<CollectionAccessEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionAccessEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionAccessed(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionAddToEvent.class, new InteractionEventDispatcherTypeSafe<CollectionAddToEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionAddToEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionAddedTo(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionRemoveFromEvent.class, new InteractionEventDispatcherTypeSafe<CollectionRemoveFromEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionRemoveFromEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionRemovedFrom(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(ActionVisibilityEvent.class, new InteractionEventDispatcherTypeSafe<ActionVisibilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ActionVisibilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.actionVisible(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(ActionUsabilityEvent.class, new InteractionEventDispatcherTypeSafe<ActionUsabilityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ActionUsabilityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.actionUsable(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(ActionArgumentEvent.class, new InteractionEventDispatcherTypeSafe<ActionArgumentEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ActionArgumentEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.actionArgument(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(ActionInvocationEvent.class, new InteractionEventDispatcherTypeSafe<ActionInvocationEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ActionInvocationEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.actionInvoked(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(ObjectValidityEvent.class, new InteractionEventDispatcherTypeSafe<ObjectValidityEvent>() {
-            @Override
-            public void dispatchTypeSafe(final ObjectValidityEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.objectPersisted(interactionEvent);
-                }
-            }
-        });
-        dispatchersByEventClass.put(CollectionMethodEvent.class, new InteractionEventDispatcherTypeSafe<CollectionMethodEvent>() {
-            @Override
-            public void dispatchTypeSafe(final CollectionMethodEvent interactionEvent) {
-                for (final InteractionListener l : getListeners()) {
-                    l.collectionMethodInvoked(interactionEvent);
-                }
-            }
-        });
-    }
-
-    // /////////////////////////////////////////////////////////////
-    // wrap and unwrap
-    // /////////////////////////////////////////////////////////////
-
-    @Override
-    public <T> T wrap(final T domainObject) {
-        return wrap(domainObject, ExecutionMode.EXECUTE);
-    }
-
-    @Override
-    public <T> T wrap(final T domainObject, final ExecutionMode mode) {
-        if (isWrapper(domainObject)) {
-            return domainObject;
-        }
-        return Proxy.proxy(domainObject, this, mode, authenticationSessionProvider, specificationLookup, adapterManager, objectPersistor);
-    }
-
-    @Override
-    public boolean isWrapper(final Object possibleWrappedDomainObject) {
-        return possibleWrappedDomainObject instanceof WrapperObject;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    @Programmatic
-    public <T> T unwrap(T possibleWrappedDomainObject) {
-        if(isWrapper(possibleWrappedDomainObject)) {
-            WrapperObject wrapperObject = (WrapperObject) possibleWrappedDomainObject;
-            return (T) wrapperObject.wrapped();
-        } 
-        return possibleWrappedDomainObject;
-    }
-
-    // /////////////////////////////////////////////////////////////
-    // Listeners
-    // /////////////////////////////////////////////////////////////
-
-    @Override
-    public List<InteractionListener> getListeners() {
-        return listeners;
-    }
-
-    @Override
-    public boolean addInteractionListener(final InteractionListener listener) {
-        return listeners.add(listener);
-    }
-
-    @Override
-    public boolean removeInteractionListener(final InteractionListener listener) {
-        return listeners.remove(listener);
-    }
-
-    @Override
-    public void notifyListeners(final InteractionEvent interactionEvent) {
-        final InteractionEventDispatcher dispatcher = dispatchersByEventClass.get(interactionEvent.getClass());
-        if (dispatcher == null) {
-            throw new RuntimeException("Unknown InteractionEvent - register into dispatchers map");
-        }
-        dispatcher.dispatch(interactionEvent);
-    }
-
-    // /////////////////////////////////////////////////////////////
-    // Listeners
-    // /////////////////////////////////////////////////////////////
-
-    @Programmatic
-    @Override
-    public void setAuthenticationSessionProvider(final AuthenticationSessionProvider authenticationSessionProvider) {
-        this.authenticationSessionProvider = authenticationSessionProvider;
-    }
-
-    @Programmatic
-    @Override
-    public void setAdapterManager(final AdapterManager adapterManager) {
-        this.adapterManager = adapterManager;
-    }
-
-    @Programmatic
-    @Override
-    public void setSpecificationLookup(final SpecificationLoader specificationLookup) {
-        this.specificationLookup = specificationLookup;
-    }
-
-    @Programmatic
-    @Override
-    public void setObjectPersistor(final ObjectPersistor objectPersistor) {
-        this.objectPersistor = objectPersistor;
-    }
-
 
+public class WrapperFactoryDefault extends WrapperFactoryJavassist {
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryJavassist.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryJavassist.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryJavassist.java
new file mode 100644
index 0000000..e71580f
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/WrapperFactoryJavassist.java
@@ -0,0 +1,30 @@
+/*
+ *  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.wrapper;
+
+import org.apache.isis.core.wrapper.proxy.ProxyInstantiatorForJavassist;
+
+public class WrapperFactoryJavassist extends WrapperFactoryAbstract  {
+
+    public WrapperFactoryJavassist() {
+        super(new ProxyInstantiatorForJavassist());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcher.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcher.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcher.java
new file mode 100644
index 0000000..2dfa70c
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcher.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.wrapper.dispatchers;
+
+import org.apache.isis.applib.events.InteractionEvent;
+
+public interface InteractionEventDispatcher {
+
+    void dispatch(InteractionEvent interactionEvent);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcherTypeSafe.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcherTypeSafe.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcherTypeSafe.java
new file mode 100644
index 0000000..c568633
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/dispatchers/InteractionEventDispatcherTypeSafe.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.wrapper.dispatchers;
+
+import org.apache.isis.applib.events.InteractionEvent;
+
+public abstract class InteractionEventDispatcherTypeSafe<T extends InteractionEvent> implements InteractionEventDispatcher {
+
+    public abstract void dispatchTypeSafe(T interactionEvent);
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void dispatch(final InteractionEvent interactionEvent) {
+        dispatchTypeSafe((T) interactionEvent);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/AbstractCollectionInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/AbstractCollectionInvocationHandler.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/AbstractCollectionInvocationHandler.java
new file mode 100644
index 0000000..ab1e808
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/AbstractCollectionInvocationHandler.java
@@ -0,0 +1,89 @@
+/*
+ *  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.wrapper.handlers;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.isis.applib.events.CollectionMethodEvent;
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+abstract class AbstractCollectionInvocationHandler<T, C> extends DelegatingInvocationHandlerDefault<C> {
+
+    private final List<Method> interceptedMethods = new ArrayList<Method>();
+    private final List<Method> vetoedMethods = new ArrayList<Method>();
+    private final String collectionName;
+    private final OneToManyAssociation oneToManyAssociation;
+    private final T domainObject;
+
+    public AbstractCollectionInvocationHandler(final C collectionOrMapToProxy, final String collectionName, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) {
+        super(collectionOrMapToProxy, handler.getHeadlessViewer(), handler.getExecutionMode());
+        this.collectionName = collectionName;
+        this.oneToManyAssociation = otma;
+        this.domainObject = handler.getDelegate();
+    }
+
+    protected Method intercept(final Method method) {
+        this.interceptedMethods.add(method);
+        return method;
+    }
+
+    protected Method veto(final Method method) {
+        this.vetoedMethods.add(method);
+        return method;
+    }
+
+    public String getCollectionName() {
+        return collectionName;
+    }
+
+    public OneToManyAssociation getCollection() {
+        return oneToManyAssociation;
+    }
+
+    public T getDomainObject() {
+        return domainObject;
+    }
+
+    @Override
+    public Object invoke(final Object collectionObject, final Method method, final Object[] args) throws Throwable {
+
+        // delegate
+        final Object returnValueObj = delegate(method, args);
+
+        if (interceptedMethods.contains(method)) {
+
+            resolveIfRequired(domainObject);
+
+            final InteractionEvent ev = new CollectionMethodEvent(getDelegate(), getCollection().getIdentifier(), getDomainObject(), method.getName(), args, returnValueObj);
+            notifyListeners(ev);
+            return returnValueObj;
+        }
+
+        if (vetoedMethods.contains(method)) {
+            throw new UnsupportedOperationException(String.format("Method '%s' may not be called directly.", method.getName()));
+        }
+
+        return returnValueObj;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/CollectionInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/CollectionInvocationHandler.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/CollectionInvocationHandler.java
new file mode 100644
index 0000000..bcf2973
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/CollectionInvocationHandler.java
@@ -0,0 +1,53 @@
+/*
+ *  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.wrapper.handlers;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.isis.core.commons.lang.ObjectExtensions;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+class CollectionInvocationHandler<T, R> extends AbstractCollectionInvocationHandler<T, R> {
+
+    public CollectionInvocationHandler(final R collectionToProxy, final String collectionName, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) {
+        super(collectionToProxy, collectionName, handler, otma);
+
+        try {
+            intercept(ObjectExtensions.getMethod(collectionToProxy, "contains", Object.class));
+            intercept(ObjectExtensions.getMethod(collectionToProxy, "size"));
+            intercept(ObjectExtensions.getMethod(collectionToProxy, "isEmpty"));
+            if (collectionToProxy instanceof List) {
+                intercept(ObjectExtensions.getMethod(collectionToProxy, "get", int.class));
+            }
+            veto(ObjectExtensions.getMethod(collectionToProxy, "add", Object.class));
+            veto(ObjectExtensions.getMethod(collectionToProxy, "remove", Object.class));
+            veto(ObjectExtensions.getMethod(collectionToProxy, "addAll", Collection.class));
+            veto(ObjectExtensions.getMethod(collectionToProxy, "removeAll", Collection.class));
+            veto(ObjectExtensions.getMethod(collectionToProxy, "retainAll", Collection.class));
+            veto(ObjectExtensions.getMethod(collectionToProxy, "clear"));
+        } catch (final NoSuchMethodException e) {
+            // ///CLOVER:OFF
+            throw new RuntimeException("A Collection method could not be found: " + e.getMessage());
+            // ///CLOVER:ON
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandler.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandler.java
new file mode 100644
index 0000000..d424f64
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandler.java
@@ -0,0 +1,32 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.wrapper.handlers;
+
+import java.lang.reflect.InvocationHandler;
+
+public interface DelegatingInvocationHandler<T> extends InvocationHandler {
+
+    T getDelegate();
+
+    public boolean isResolveObjectChangedEnabled();
+
+    public void setResolveObjectChangedEnabled(boolean resolveObjectChangedEnabled);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/074d2c4e/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandlerDefault.java
----------------------------------------------------------------------
diff --git a/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandlerDefault.java b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandlerDefault.java
new file mode 100644
index 0000000..e1b7cdd
--- /dev/null
+++ b/core/wrapper/src/main/java/org/apache/isis/core/wrapper/handlers/DelegatingInvocationHandlerDefault.java
@@ -0,0 +1,131 @@
+/*
+ *  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.wrapper.handlers;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.runtime.persistence.container.DomainObjectContainerObjectChanged;
+import org.apache.isis.core.runtime.persistence.container.DomainObjectContainerResolve;
+
+public class DelegatingInvocationHandlerDefault<T> implements DelegatingInvocationHandler<T> {
+
+    private final T delegate;
+    protected final WrapperFactory wrapperFactory;
+    private final ExecutionMode executionMode;
+
+    protected final Method equalsMethod;
+    protected final Method hashCodeMethod;
+    protected final Method toStringMethod;
+
+    private final DomainObjectContainerObjectChanged domainObjectContainerObjectChanged;
+    private final DomainObjectContainerResolve domainObjectContainerResolve;
+
+    private boolean resolveObjectChangedEnabled;
+
+    public DelegatingInvocationHandlerDefault(final T delegate, final WrapperFactory headlessViewer, final ExecutionMode executionMode) {
+        if (delegate == null) {
+            throw new IllegalArgumentException("delegate must not be null");
+        }
+        this.delegate = delegate;
+        this.wrapperFactory = headlessViewer;
+        this.executionMode = executionMode;
+
+        this.domainObjectContainerResolve = new DomainObjectContainerResolve();
+        this.domainObjectContainerObjectChanged = new DomainObjectContainerObjectChanged();
+
+        try {
+            equalsMethod = delegate.getClass().getMethod("equals", new Class[] { Object.class });
+            hashCodeMethod = delegate.getClass().getMethod("hashCode", new Class[] {});
+            toStringMethod = delegate.getClass().getMethod("toString", new Class[] {});
+        } catch (final NoSuchMethodException e) {
+            // ///CLOVER:OFF
+            throw new RuntimeException("An Object method could not be found: " + e.getMessage());
+            // ///CLOVER:ON
+        }
+    }
+
+    @Override
+    public boolean isResolveObjectChangedEnabled() {
+        return resolveObjectChangedEnabled;
+    }
+
+    @Override
+    public void setResolveObjectChangedEnabled(final boolean resolveObjectChangedEnabled) {
+        this.resolveObjectChangedEnabled = resolveObjectChangedEnabled;
+    }
+
+    protected void resolveIfRequired(final ObjectAdapter targetAdapter) {
+        resolveIfRequired(targetAdapter.getObject());
+    }
+
+    protected void resolveIfRequired(final Object domainObject) {
+        if (resolveObjectChangedEnabled) {
+            domainObjectContainerResolve.resolve(domainObject);
+        }
+    }
+
+    protected void objectChangedIfRequired(final ObjectAdapter targetAdapter) {
+        objectChangedIfRequired(targetAdapter.getObject());
+    }
+
+    protected void objectChangedIfRequired(final Object domainObject) {
+        if (resolveObjectChangedEnabled) {
+            domainObjectContainerObjectChanged.objectChanged(domainObject);
+        }
+    }
+
+    public WrapperFactory getHeadlessViewer() {
+        return wrapperFactory;
+    }
+
+    @Override
+    public T getDelegate() {
+        return delegate;
+    }
+
+    public ExecutionMode getExecutionMode() {
+        return executionMode;
+    }
+
+    protected Object delegate(final Method method, final Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+
+        return method.invoke(getDelegate(), args);
+    }
+
+    protected boolean isObjectMethod(final Method method) {
+        return toStringMethod.equals(method) || hashCodeMethod.equals(method) || equalsMethod.equals(method);
+    }
+
+    @Override
+    public Object invoke(final Object object, final Method method, final Object[] args) throws Throwable {
+        return method.invoke(object, args);
+    }
+
+    protected InteractionEvent notifyListeners(final InteractionEvent interactionEvent) {
+        wrapperFactory.notifyListeners(interactionEvent);
+        return interactionEvent;
+    }
+
+}