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/08/20 10:26:12 UTC

git commit: ISIS-210: choices with param types now also supported.

Updated Branches:
  refs/heads/master fbc5e0b4a -> 07d99321c


ISIS-210: choices with param types now also supported.

* changes to ObjectActionParameterContributee subtypes and ActionParameterChoicesFacet to support choices taking parameter types
* refactored to simplify impl of defaults also

in addition:
* updated ToDo example, factored out ToDoItemContributions to demonstrate usage with a new "updateCategory" action
* combined ToDoItemsJdo with ToDoItems service
* new installAndReturnFirst() prototype action

also:
* fixed minor issue with wicket dropdown; the current value must be in the list of choices, else will NPE, so adds automatically
* enhanced rendering of actions to show @DescribedAs as a title


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

Branch: refs/heads/master
Commit: 07d99321cdcb894d26e0f1b61d78195c7f83809c
Parents: fbc5e0b
Author: Dan Haywood <da...@apache.org>
Authored: Tue Aug 20 09:25:19 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Tue Aug 20 09:25:19 2013 +0100

----------------------------------------------------------------------
 .../components/widgets/cssmenu/CssMenuItem.java |  31 +++-
 .../valuechoices/ValueChoicesSelect2Panel.java  |   6 +
 .../choices/ActionParameterChoicesFacet.java    |   2 +-
 .../specloader/specimpl/ObjectActionImpl.java   |   2 +-
 .../specimpl/ObjectActionParameterAbstract.java | 159 +++++++++-------
 ...jectActionParameterParseableContributee.java |  43 ++---
 .../OneToOneActionParameterContributee.java     |  35 ++--
 .../ActionParameterChoicesFacetNone.java        |   2 +-
 ...eterChoicesFacetDerivedFromChoicesFacet.java |   2 +-
 .../ActionParameterChoicesFacetViaMethod.java   |   4 +-
 .../quickstart_wicket_restful_jdo/dom/pom.xml   |   2 +-
 .../dom/todo/ToDoItemChangedPayloadFactory.java |   4 +
 .../java/dom/todo/ToDoItemContributions.java    | 181 +++++++++++++++++++
 .../dom/src/main/java/dom/todo/ToDoItems.java   | 149 ++++++---------
 .../src/main/java/dom/todo/ToDoItemsJdo.java    |  87 ---------
 .../java/integration/ToDoSystemInitializer.java |   9 +-
 .../app/services/ToDoItemsFixturesService.java  |  18 ++
 .../src/main/webapp/WEB-INF/isis.properties     |   3 +-
 18 files changed, 434 insertions(+), 305 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
index f31cc23..413abfb 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
@@ -45,6 +45,7 @@ import org.apache.isis.core.commons.ensure.Ensure;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.facets.describedas.DescribedAsFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -94,6 +95,10 @@ public class CssMenuItem implements Serializable {
             return this;
         }
 
+        public Builder describedAs(String descriptionIfAny) {
+            cssMenuItem.setDescription(descriptionIfAny);
+            return this;
+        }
 
         public Builder returnsBlobOrClob(boolean blobOrClob) {
             cssMenuItem.setReturnsBlobOrClob(blobOrClob);
@@ -133,6 +138,7 @@ public class CssMenuItem implements Serializable {
             }
             return cssMenuItem;
         }
+
     }
 
     private final String name;
@@ -152,6 +158,8 @@ public class CssMenuItem implements Serializable {
     private String actionIdentifier;
     private String cssClass;
 
+    private String description;
+
 
 
     /**
@@ -161,6 +169,7 @@ public class CssMenuItem implements Serializable {
         return new Builder(name);
     }
 
+
     public void setActionIdentifier(String actionIdentifier) {
         this.actionIdentifier = actionIdentifier;
     }
@@ -218,7 +227,6 @@ public class CssMenuItem implements Serializable {
         this.blobOrClob = blobOrClob;
     }
 
-
     /**
      * Only populated if not {@link #isEnabled() enabled}.
      */
@@ -230,6 +238,13 @@ public class CssMenuItem implements Serializable {
         this.disabledReason = disabledReason;
     }
 
+    public String getDescription() {
+        return description;
+    }
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
     // //////////////////////////////////////////////////////////////
     // To add submenu items
     // //////////////////////////////////////////////////////////////
@@ -267,18 +282,21 @@ public class CssMenuItem implements Serializable {
         final AbstractLink link = linkAndLabel.getLink();
         final String actionLabel = linkAndLabel.getLabel();
 
-        // check whether enabled
         final Consent usability = objectAction.isUsable(session, adapter, where);
         final String reasonDisabledIfAny = usability.getReason();
-        CssClassFacet cssClassFacet = objectAction.getFacet(CssClassFacet.class);
+        
+        final DescribedAsFacet describedAsFacet = objectAction.getFacet(DescribedAsFacet.class);
+        final String descriptionIfAny = describedAsFacet != null? describedAsFacet.value(): null;
+        
+        final CssClassFacet cssClassFacet = objectAction.getFacet(CssClassFacet.class);
 
-        // check if returns blob or clob (if so, then add CSS to suppress veil)
         final boolean blobOrClob = returnsBlobOrClob(objectAction);
         final boolean prototype = isExplorationOrPrototype(objectAction);
         final String actionIdentifier = actionIdentifierFor(objectAction);
 
         Builder builder = newSubMenuItem(actionLabel)
                 .link(link)
+                .describedAs(descriptionIfAny)
                 .enabled(reasonDisabledIfAny)
                 .returnsBlobOrClob(blobOrClob)
                 .prototyping(prototype)
@@ -357,7 +375,10 @@ public class CssMenuItem implements Serializable {
             // show link...
             markupContainer.add(link);
             link.add(label);
-            
+
+            if(this.description != null) {
+                label.add(new AttributeModifier("title", Model.of(description)));
+            }
             if(this.blobOrClob) {
                 link.add(new CssClassAppender("noVeil"));
             }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ValueChoicesSelect2Panel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ValueChoicesSelect2Panel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ValueChoicesSelect2Panel.java
index 7b907c9..e711780 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ValueChoicesSelect2Panel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/valuechoices/ValueChoicesSelect2Panel.java
@@ -229,6 +229,12 @@ public class ValueChoicesSelect2Panel extends ScalarPanelAbstract { // ScalarPan
         // take a copy otherwise is only lazily evaluated
         final List<ObjectAdapterMemento> choicesMementos = Lists.newArrayList(Lists.transform(choices, Mementos.fromAdapter()));
         
+        final ObjectAdapterMemento currentValue = getModel().getObjectAdapterMemento();
+        if(currentValue != null && !choicesMementos.contains(currentValue)) {
+            choicesMementos.add(currentValue);
+        }
+        
+
         return new ModelAbstract<List<ObjectAdapterMemento>>(choicesMementos){
             private static final long serialVersionUID = 1L;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/choices/ActionParameterChoicesFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/choices/ActionParameterChoicesFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/choices/ActionParameterChoicesFacet.java
index 341d529..4e40114 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/choices/ActionParameterChoicesFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/choices/ActionParameterChoicesFacet.java
@@ -32,5 +32,5 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
  */
 public interface ActionParameterChoicesFacet extends Facet {
 
-    public Object[] getChoices(ObjectAdapter inObject);
+    public Object[] getChoices(ObjectAdapter target, ObjectAdapter[] arguments);
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionImpl.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionImpl.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionImpl.java
index 3e7b68c..deeaa23 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionImpl.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionImpl.java
@@ -443,7 +443,7 @@ public class ObjectActionImpl extends ObjectMemberAbstract implements ObjectActi
             for (int i = 0; i < parameterCount; i++) {
                 final ActionParameterChoicesFacet paramFacet = parameters.get(i).getFacet(ActionParameterChoicesFacet.class);
                 if (paramFacet != null && !paramFacet.isNoop()) {
-                    parameterChoicesPojos[i] = paramFacet.getChoices(target);
+                    parameterChoicesPojos[i] = paramFacet.getChoices(target, null);
                 } else {
                     parameterChoicesPojos[i] = new Object[0];
                 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
index 3d10236..1da4772 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterAbstract.java
@@ -244,40 +244,10 @@ public abstract class ObjectActionParameterAbstract implements ObjectActionParam
         }
     }
 
-    // //////////////////////////////////////////////////////////
-    // Interaction
-    // //////////////////////////////////////////////////////////
-
-    @Override
-    public ActionArgumentContext createProposedArgumentInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final ObjectAdapter targetObject, final ObjectAdapter[] proposedArguments, final int position) {
-        return new ActionArgumentContext(getDeploymentCategory(), getAuthenticationSession(), invocationMethod, targetObject, getIdentifier(), proposedArguments, position);
-    }
 
-
-    @Override
-    public boolean hasChoices() {
-        final ActionParameterChoicesFacet choicesFacet = getFacet(ActionParameterChoicesFacet.class);
-
-        return choicesFacet != null || BoundedFacetUtils.isBoundedSet(getSpecification());
-    }
-
-    @Override
-    public ObjectAdapter[] getChoices(final ObjectAdapter adapter) {
-        final List<ObjectAdapter> parameterChoices = Lists.newArrayList();
-        final ActionParameterChoicesFacet choicesFacet = getFacet(ActionParameterChoicesFacet.class);
-
-        if (choicesFacet != null) {
-            final Object[] choices = choicesFacet.getChoices(adapter);
-            checkChoicesOrAutoCompleteType(getSpecificationLookup(), choices, getSpecification());
-            for (final Object choice : choices) {
-                parameterChoices.add(getAdapterMap().adapterFor(choice));
-            }
-        }
-        if (parameterChoices.size() == 0 && BoundedFacetUtils.isBoundedSet(getSpecification())) {
-            addParameterChoicesForBounded(parameterChoices);
-        }
-        return parameterChoices.toArray(new ObjectAdapter[0]);
-    }
+    // /////////////////////////////////////////////////////////////
+    // AutoComplete
+    // /////////////////////////////////////////////////////////////
 
     @Override
     public boolean hasAutoComplete() {
@@ -287,20 +257,20 @@ public abstract class ObjectActionParameterAbstract implements ObjectActionParam
 
     @Override
     public ObjectAdapter[] getAutoComplete(ObjectAdapter adapter, String searchArg) {
-        final List<ObjectAdapter> autoCompleteChoiceAdapters = Lists.newArrayList();
+        final List<ObjectAdapter> adapters = Lists.newArrayList();
         final ActionParameterAutoCompleteFacet facet = getFacet(ActionParameterAutoCompleteFacet.class);
 
         if (facet != null) {
-            final Object[] autoCompleteChoices = facet.autoComplete(adapter, searchArg);
-            checkChoicesOrAutoCompleteType(getSpecificationLookup(), autoCompleteChoices, getSpecification());
-            for (final Object autoCompleteChoice : autoCompleteChoices) {
-                autoCompleteChoiceAdapters.add(getAdapterMap().adapterFor(autoCompleteChoice));
+            final Object[] choices = facet.autoComplete(adapter, searchArg);
+            checkChoicesOrAutoCompleteType(getSpecificationLookup(), choices, getSpecification());
+            for (final Object choice : choices) {
+                adapters.add(getAdapterMap().adapterFor(choice));
             }
         }
-        if (autoCompleteChoiceAdapters.size() == 0 && BoundedFacetUtils.isBoundedSet(getSpecification())) {
-            addParameterChoicesForBounded(autoCompleteChoiceAdapters);
+        if (adapters.size() == 0 && BoundedFacetUtils.isBoundedSet(getSpecification())) {
+            addAllInstancesForType(adapters);
         }
-        return autoCompleteChoiceAdapters.toArray(new ObjectAdapter[0]);
+        return adapters.toArray(new ObjectAdapter[0]);
     }
 
     @Override
@@ -309,37 +279,64 @@ public abstract class ObjectActionParameterAbstract implements ObjectActionParam
         return facet != null? facet.getMinLength(): MinLengthUtil.MIN_LENGTH_DEFAULT;
     }
 
-    private <T> void addParameterChoicesForBounded(final List<ObjectAdapter> parameterChoices) {
-        final Query<T> query = new QueryFindAllInstances<T>(getSpecification().getFullIdentifier());
-        final List<ObjectAdapter> allInstancesAdapter = getQuerySubmitter().allMatchingQuery(query);
-        for (final ObjectAdapter choiceAdapter : allInstancesAdapter) {
-            parameterChoices.add(choiceAdapter);
-        }
+
+    // /////////////////////////////////////////////////////////////
+    // Choices
+    // /////////////////////////////////////////////////////////////
+
+    @Override
+    public boolean hasChoices() {
+        final ActionParameterChoicesFacet choicesFacet = getFacet(ActionParameterChoicesFacet.class);
+        return choicesFacet != null || BoundedFacetUtils.isBoundedSet(getSpecification());
     }
 
-    protected static void checkChoicesOrAutoCompleteType(final SpecificationLoader specificationLookup, final Object[] objects, final ObjectSpecification paramSpec) {
-        for (final Object object : objects) {
-            final ObjectSpecification componentSpec = specificationLookup.loadSpecification(object.getClass());
-            if (!componentSpec.isOfType(paramSpec)) {
-                throw new DomainModelException("Type incompatible with parameter type; expected " + paramSpec.getFullIdentifier() + ", but was " + componentSpec.getFullIdentifier());
+    @Override
+    public ObjectAdapter[] getChoices(final ObjectAdapter adapter) {
+        final ObjectAdapter target = targetForDefaultOrChoices(adapter);
+        final ObjectAdapter[] args = argsForDefaultOrChoices(adapter);
+        
+        return getChoices(target, args);
+    }
+
+    private ObjectAdapter[] getChoices(final ObjectAdapter target, final ObjectAdapter[] args) {
+        final List<ObjectAdapter> adapters = Lists.newArrayList();
+        final ActionParameterChoicesFacet facet = getFacet(ActionParameterChoicesFacet.class);
+
+        if (facet != null) {
+            final Object[] choices = facet.getChoices(target, args);
+            checkChoicesOrAutoCompleteType(getSpecificationLookup(), choices, getSpecification());
+            for (final Object choice : choices) {
+                adapters.add(getAdapterMap().adapterFor(choice));
             }
         }
+        if (adapters.size() == 0 && BoundedFacetUtils.isBoundedSet(getSpecification())) {
+            addAllInstancesForType(adapters);
+        }
+        return adapters.toArray(new ObjectAdapter[0]);
     }
+    
+    // /////////////////////////////////////////////////////////////
+    // Defaults
+    // /////////////////////////////////////////////////////////////
 
     @Override
     public ObjectAdapter getDefault(final ObjectAdapter adapter) {
-        if (false /*parentAction.isContributed()*/ && adapter != null) {
-            if (adapter.getSpecification().isOfType(getSpecification())) {
-                return adapter;
-            }
-        }
+        
+        final ObjectAdapter target = targetForDefaultOrChoices(adapter);;
+        final ObjectAdapter[] args = argsForDefaultOrChoices(adapter);
+        
+        return getDefault(target, args);
+    }
+
+    private ObjectAdapter getDefault(
+            final ObjectAdapter target, 
+            final ObjectAdapter[] args) {
         final ActionParameterDefaultsFacet defaultsFacet = getFacet(ActionParameterDefaultsFacet.class);
         if (defaultsFacet != null) {
-            final Object dflt = defaultsFacet.getDefault(adapter, null);
+            final Object dflt = defaultsFacet.getDefault(target, args);
             if (dflt == null) {
                 // it's possible that even though there is a default facet, when
-                // invoked it
-                // is unable to return a default.
+                // invoked it is unable to return a default.
                 return null;
             }
             return getAdapterMap().adapterFor(dflt);
@@ -347,17 +344,51 @@ public abstract class ObjectActionParameterAbstract implements ObjectActionParam
         return null;
     }
 
-    protected AuthenticationSession getAuthenticationSession() {
-        return getAuthenticationSessionProvider().getAuthenticationSession();
+    /**
+     * Hook method; {@link ObjectActionParameterContributee contributed action parameter}s override.
+     */
+    protected ObjectAdapter targetForDefaultOrChoices(ObjectAdapter adapter) {
+        return adapter;
+    }
+
+    /**
+     * Hook method; {@link ObjectActionParameterContributee contributed action parameter}s override.
+     */
+    protected ObjectAdapter[] argsForDefaultOrChoices(final ObjectAdapter adapter) {
+        return null;
     }
 
     
+    // helpers
+
+    static void checkChoicesOrAutoCompleteType(final SpecificationLoader specificationLookup, final Object[] objects, final ObjectSpecification paramSpec) {
+        for (final Object object : objects) {
+            final ObjectSpecification componentSpec = specificationLookup.loadSpecification(object.getClass());
+            if (!componentSpec.isOfType(paramSpec)) {
+                throw new DomainModelException("Type incompatible with parameter type; expected " + paramSpec.getFullIdentifier() + ", but was " + componentSpec.getFullIdentifier());
+            }
+        }
+    }
+
+    private <T> void addAllInstancesForType(final List<ObjectAdapter> adapters) {
+        final Query<T> query = new QueryFindAllInstances<T>(getSpecification().getFullIdentifier());
+        final List<ObjectAdapter> allInstancesAdapter = getQuerySubmitter().allMatchingQuery(query);
+        for (final ObjectAdapter choiceAdapter : allInstancesAdapter) {
+            adapters.add(choiceAdapter);
+        }
+    }
+
     
     // /////////////////////////////////////////////////////////////
     // Validation
     // /////////////////////////////////////////////////////////////
 
     @Override
+    public ActionArgumentContext createProposedArgumentInteractionContext(final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final ObjectAdapter targetObject, final ObjectAdapter[] proposedArguments, final int position) {
+        return new ActionArgumentContext(getDeploymentCategory(), getAuthenticationSession(), invocationMethod, targetObject, getIdentifier(), proposedArguments, position);
+    }
+
+    @Override
     public String isValid(final ObjectAdapter adapter, final Object proposedValue, final Localization localization) {
         
         ObjectAdapter proposedValueAdapter = null;
@@ -435,4 +466,8 @@ public abstract class ObjectActionParameterAbstract implements ObjectActionParam
         return parentAction.getQuerySubmitter();
     }
 
+    protected AuthenticationSession getAuthenticationSession() {
+        return getAuthenticationSessionProvider().getAuthenticationSession();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterParseableContributee.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterParseableContributee.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterParseableContributee.java
index 15ab521..cad997b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterParseableContributee.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionParameterParseableContributee.java
@@ -16,7 +16,13 @@
  */
 package org.apache.isis.core.metamodel.specloader.specimpl;
 
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.object.bounded.BoundedFacetUtils;
+import org.apache.isis.core.metamodel.facets.param.choices.ActionParameterChoicesFacet;
 import org.apache.isis.core.metamodel.facets.param.defaults.ActionParameterDefaultsFacet;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 
@@ -33,21 +39,24 @@ public class ObjectActionParameterParseableContributee extends ObjectActionParam
     private final ObjectActionParameter serviceActionParameter;
     @SuppressWarnings("unused")
     private final int serviceParamNumber;
-    private final ObjectActionContributee objectAction;
+    @SuppressWarnings("unused")
+    private final int contributeeParamNumber;
+    private final ObjectActionContributee contributeeAction;
 
     public ObjectActionParameterParseableContributee(
             final ObjectAdapter serviceAdapter,
             final ObjectActionImpl serviceAction,
             final ObjectActionParameterAbstract serviceActionParameter,
             final int serviceParamNumber,
-            final int number,
-            final ObjectActionContributee objectAction) {
-        super(number, objectAction, serviceActionParameter.getPeer());
+            final int contributeeParamNumber,
+            final ObjectActionContributee contributeeAction) {
+        super(contributeeParamNumber, contributeeAction, serviceActionParameter.getPeer());
         this.serviceAdapter = serviceAdapter;
         this.serviceAction = serviceAction;
         this.serviceActionParameter = serviceActionParameter;
         this.serviceParamNumber = serviceParamNumber;
-        this.objectAction = objectAction;
+        this.contributeeParamNumber = contributeeParamNumber;
+        this.contributeeAction = contributeeAction;
     }
 
     @Override
@@ -55,28 +64,14 @@ public class ObjectActionParameterParseableContributee extends ObjectActionParam
         return serviceActionParameter.getAutoComplete(serviceAdapter, searchArg);
     }
 
-    @Override
-    public ObjectAdapter[] getChoices(final ObjectAdapter adapter) {
-        return serviceActionParameter.getChoices(serviceAdapter);
+    protected ObjectAdapter targetForDefaultOrChoices(ObjectAdapter adapter) {
+        return serviceAdapter;
     }
 
-    @Override
-    public ObjectAdapter getDefault(final ObjectAdapter adapter) {
-        
+    protected ObjectAdapter[] argsForDefaultOrChoices(final ObjectAdapter adapter) {
         ObjectAdapter[] args = new ObjectAdapter[serviceAction.getParameterCount()];
-        args[objectAction.getContributeeParam()] = adapter;
-        
-        final ActionParameterDefaultsFacet defaultsFacet = serviceActionParameter.getFacet(ActionParameterDefaultsFacet.class);
-        if (defaultsFacet != null) {
-            final Object dflt = defaultsFacet.getDefault(serviceAdapter, args);
-            if (dflt == null) {
-                return null;
-            }
-            return getAdapterMap().adapterFor(dflt);
-        }
-        return null;
+        args[contributeeAction.getContributeeParam()] = adapter;
+        return args;
     }
-
-    
     
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneActionParameterContributee.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneActionParameterContributee.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneActionParameterContributee.java
index dfc06fb..b706a1c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneActionParameterContributee.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneActionParameterContributee.java
@@ -27,21 +27,24 @@ public class OneToOneActionParameterContributee extends OneToOneActionParameterI
     private final ObjectActionParameter serviceActionParameter;
     @SuppressWarnings("unused")
     private final int serviceParamNumber;
-    private final ObjectActionContributee objectAction;
+    @SuppressWarnings("unused")
+    private final int contributeeParamNumber;
+    private final ObjectActionContributee contributeeAction;
 
     public OneToOneActionParameterContributee(
             final ObjectAdapter serviceAdapter,
             final ObjectActionImpl serviceAction,
             final ObjectActionParameterAbstract serviceActionParameter,
             final int serviceParamNumber,
-            final int number,
-            final ObjectActionContributee objectAction) {
-        super(number, objectAction, serviceActionParameter.getPeer());
+            final int contributeeParamNumber,
+            final ObjectActionContributee contributeeAction) {
+        super(contributeeParamNumber, contributeeAction, serviceActionParameter.getPeer());
         this.serviceAdapter = serviceAdapter;
         this.serviceAction = serviceAction;
         this.serviceActionParameter = serviceActionParameter;
         this.serviceParamNumber = serviceParamNumber;
-        this.objectAction = objectAction;
+        this.contributeeParamNumber = contributeeParamNumber;
+        this.contributeeAction = contributeeAction;
     }
 
     @Override
@@ -49,26 +52,14 @@ public class OneToOneActionParameterContributee extends OneToOneActionParameterI
         return serviceActionParameter.getAutoComplete(serviceAdapter, searchArg);
     }
 
-    @Override
-    public ObjectAdapter[] getChoices(final ObjectAdapter adapter) {
-        return serviceActionParameter.getChoices(serviceAdapter);
+    protected ObjectAdapter targetForDefaultOrChoices(ObjectAdapter adapter) {
+        return serviceAdapter;
     }
 
-    @Override
-    public ObjectAdapter getDefault(final ObjectAdapter adapter) {
-        
+    protected ObjectAdapter[] argsForDefaultOrChoices(final ObjectAdapter adapter) {
         ObjectAdapter[] args = new ObjectAdapter[serviceAction.getParameterCount()];
-        args[objectAction.getContributeeParam()] = serviceAdapter;
-        
-        final ActionParameterDefaultsFacet defaultsFacet = serviceActionParameter.getFacet(ActionParameterDefaultsFacet.class);
-        if (defaultsFacet != null) {
-            final Object dflt = defaultsFacet.getDefault(serviceAdapter, args);
-            if (dflt == null) {
-                return null;
-            }
-            return getAdapterMap().adapterFor(dflt);
-        }
-        return null;
+        args[contributeeAction.getContributeeParam()] = adapter;
+        return args;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/ActionParameterChoicesFacetNone.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/ActionParameterChoicesFacetNone.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/ActionParameterChoicesFacetNone.java
index 8f455b1..ae1112e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/ActionParameterChoicesFacetNone.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/ActionParameterChoicesFacetNone.java
@@ -31,7 +31,7 @@ public class ActionParameterChoicesFacetNone extends ActionParameterChoicesFacet
     }
 
     @Override
-    public Object[] getChoices(final ObjectAdapter inObject) {
+    public Object[] getChoices(final ObjectAdapter adapter, final ObjectAdapter[] arguments) {
         return new ObjectAdapter[0];
     }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/enums/ActionParameterChoicesFacetDerivedFromChoicesFacet.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/enums/ActionParameterChoicesFacetDerivedFromChoicesFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/enums/ActionParameterChoicesFacetDerivedFromChoicesFacet.java
index 9edbfb83..872d274 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/enums/ActionParameterChoicesFacetDerivedFromChoicesFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/enums/ActionParameterChoicesFacetDerivedFromChoicesFacet.java
@@ -35,7 +35,7 @@ public class ActionParameterChoicesFacetDerivedFromChoicesFacet extends ActionPa
     }
 
     @Override
-    public Object[] getChoices(final ObjectAdapter adapter) {
+    public Object[] getChoices(final ObjectAdapter adapter, final ObjectAdapter[] arguments) {
         final FacetHolder facetHolder = getFacetHolder();
         final TypedHolder paramPeer = (TypedHolder) facetHolder;
         final ObjectSpecification noSpec = getSpecification(paramPeer.getType());

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/methodnum/ActionParameterChoicesFacetViaMethod.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/methodnum/ActionParameterChoicesFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/methodnum/ActionParameterChoicesFacetViaMethod.java
index 5feb056..12a9279 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/methodnum/ActionParameterChoicesFacetViaMethod.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/progmodel/facets/param/choices/methodnum/ActionParameterChoicesFacetViaMethod.java
@@ -65,8 +65,8 @@ public class ActionParameterChoicesFacetViaMethod extends ActionParameterChoices
     }
 
     @Override
-    public Object[] getChoices(final ObjectAdapter owningAdapter) {
-        final Object options = AdapterInvokeUtils.invoke(method, owningAdapter);
+    public Object[] getChoices(final ObjectAdapter adapter, final ObjectAdapter[] arguments) {
+        final Object options = AdapterInvokeUtils.invoke(method, adapter, arguments);
         if (options == null) {
             return new Object[0];
         }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/dom/pom.xml
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/pom.xml b/example/application/quickstart_wicket_restful_jdo/dom/pom.xml
index f5e4550..7ff951b 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/pom.xml
+++ b/example/application/quickstart_wicket_restful_jdo/dom/pom.xml
@@ -111,7 +111,7 @@
 		<dependency>
             <groupId>org.apache.isis.core</groupId>
 			<artifactId>isis-core-unittestsupport</artifactId>
-            <scope>true</scope>
+            <scope>test</scope>
 		</dependency>
 
         <!--

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
index f76bbef..fb4b89b 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
@@ -31,6 +31,10 @@ public class ToDoItemChangedPayloadFactory implements PayloadFactory{
             super(changed);
         }
         
+        /**
+         * Expose the item's {@link ToDoItem#getDescription() description} more explicitly
+         * in the payload.
+         */
         public String getDescription() {
             return getChanged().getDescription();
         }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
new file mode 100644
index 0000000..4d91bd1
--- /dev/null
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemContributions.java
@@ -0,0 +1,181 @@
+/*
+ *  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 dom.todo;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+
+import dom.todo.ToDoItem.Category;
+
+import org.joda.time.LocalDate;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.DescribedAs;
+import org.apache.isis.applib.annotation.Hidden;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+import org.apache.isis.applib.annotation.NotContributed;
+import org.apache.isis.applib.annotation.NotContributed.As;
+import org.apache.isis.applib.annotation.NotInServiceMenu;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.filter.Filter;
+import org.apache.isis.applib.query.QueryDefault;
+
+public class ToDoItemContributions extends AbstractFactoryAndRepository {
+
+    
+    // //////////////////////////////////////
+    // priority (contributed property)
+    // //////////////////////////////////////
+    
+    @DescribedAs("The relative priority of this item compared to others (using 'due by' date)")
+    @NotInServiceMenu
+    @MemberOrder(sequence="1")
+    @ActionSemantics(Of.SAFE)
+    @NotContributed(As.ACTION)
+    @Hidden(where=Where.ALL_TABLES)
+    public Integer priority(final ToDoItem toDoItem) {
+        if(toDoItem.isComplete()) {
+            return null;
+        }
+
+        // sort items ...
+        final List<ToDoItem> sortedNotYetComplete = 
+                ORDERING_DUE_BY
+                .compound(ORDERING_DESCRIPTION)
+                .sortedCopy(toDoItems.notYetComplete());
+        
+        // ... then locate this one
+        int i=1;
+        for (ToDoItem each : sortedNotYetComplete) {
+            if(each == toDoItem) {
+                return i;
+            }
+            i++;
+        }
+        return null;
+    }
+
+    private static Ordering<ToDoItem> ORDERING_DUE_BY = 
+        Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, LocalDate>(){
+            @Override
+            public LocalDate apply(ToDoItem input) {
+                return input.getDueBy();
+            }
+        });
+    
+    private static Ordering<ToDoItem> ORDERING_DESCRIPTION = 
+        Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, String>(){
+            @Override
+            public String apply(ToDoItem input) {
+                return input.getDescription();
+            }
+        });
+    
+    
+    // //////////////////////////////////////
+    // SimilarTo (contributed collection)
+    // //////////////////////////////////////
+    
+    @NotInServiceMenu
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(sequence="1")
+    @NotContributed(As.ACTION)
+    public List<ToDoItem> similarTo(final ToDoItem toDoItem) {
+        if(false) {
+            // the naive implementation ...
+            return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
+                @Override
+                public boolean accept(ToDoItem t) {
+                    return t != toDoItem && Objects.equal(toDoItem.getCategory(), t.getCategory()) && Objects.equal(toDoItem.getOwnedBy(), t.getOwnedBy());
+                }
+            });
+        } else {
+            // the JDO implementation ...
+            final List<ToDoItem> similarToDoItems = allMatches(
+                    new QueryDefault<ToDoItem>(ToDoItem.class, 
+                            "todo_similarTo", 
+                            "ownedBy", currentUserName(), 
+                            "category", toDoItem.getCategory()));
+            return Lists.newArrayList(Iterables.filter(similarToDoItems, excluding(toDoItem)));
+        }
+    }
+
+    private static Predicate<ToDoItem> excluding(final ToDoItem toDoItem) {
+        return new Predicate<ToDoItem>() {
+            @Override
+            public boolean apply(ToDoItem input) {
+                return input != toDoItem;
+            }
+        };
+    }
+
+    
+    // //////////////////////////////////////
+    // UpdateCategory (contributed action)
+    // //////////////////////////////////////
+
+    @DescribedAs("Demonstrates contributed actions; could also be implemented as a simple editable property")
+    @NotInServiceMenu
+    @ActionSemantics(Of.IDEMPOTENT)
+    @MemberOrder(sequence="1")
+    public ToDoItem updateCategory(ToDoItem item, @Named("Category") Category category) {
+        item.setCategory(category);
+        return item;
+    }
+
+    public List<Category> choices1UpdateCategory(ToDoItem item, Category category) {
+        // in principle we could fine-tune the choices.
+        // here, though, we just return all categories
+        return Arrays.asList(Category.values());
+    }
+    
+    public Category default1UpdateCategory(ToDoItem item, Category category) {
+        return item.getCategory();
+    }
+    
+    public String validateUpdateCategory(final ToDoItem item, Category category) {
+        return category == item.getCategory() ? "Already set to that value!" : null;
+    }
+
+    
+    // //////////////////////////////////////
+    // helpers
+    // //////////////////////////////////////
+    
+    protected String currentUserName() {
+        return getContainer().getUser().getName();
+    }
+
+    // //////////////////////////////////////
+
+    private ToDoItems toDoItems;
+    public void injectToDoItems(ToDoItems toDoItems) {
+        this.toDoItems = toDoItems;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
index d0944e7..47df583 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItems.java
@@ -20,6 +20,8 @@ package dom.todo;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -28,6 +30,7 @@ import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 
 import dom.todo.ToDoItem.Category;
@@ -50,6 +53,7 @@ import org.apache.isis.applib.annotation.RegEx;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.filter.Filter;
+import org.apache.isis.applib.query.QueryDefault;
 
 @Named("ToDos")
 public class ToDoItems extends AbstractFactoryAndRepository {
@@ -68,53 +72,66 @@ public class ToDoItems extends AbstractFactoryAndRepository {
     }
 
     // //////////////////////////////////////
-    // NotYetComplete(action)
+    // NotYetComplete (action)
     // //////////////////////////////////////
     
     @ActionSemantics(Of.SAFE)
     @MemberOrder(sequence = "1")
     public List<ToDoItem> notYetComplete() {
-        List<ToDoItem> items = doNotYetComplete();
+        final List<ToDoItem> items;
+        if(false) {
+            // the naive implementation ...
+            items = allMatches(ToDoItem.class, new Filter<ToDoItem>() {
+                @Override
+                public boolean accept(final ToDoItem t) {
+                    return ownedByCurrentUser(t) && !t.isComplete();
+                }
+            });
+        } else {
+            // the JDO implementation ...
+            items = allMatches(
+                    new QueryDefault<ToDoItem>(ToDoItem.class, 
+                            "todo_notYetComplete", 
+                            "ownedBy", currentUserName()));
+        }
         if(items.isEmpty()) {
             getContainer().informUser("All to-do items have been completed :-)");
         }
         return items;
     }
-
-    protected List<ToDoItem> doNotYetComplete() {
-        return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
-            @Override
-            public boolean accept(final ToDoItem t) {
-                return ownedByCurrentUser(t) && !t.isComplete();
-            }
-        });
-    }
     
     // //////////////////////////////////////
-    // Complete(action)
+    // Complete (action)
     // //////////////////////////////////////
     
     @ActionSemantics(Of.SAFE)
     @MemberOrder(sequence = "2")
     public List<ToDoItem> complete() {
-        List<ToDoItem> items = doComplete();
+        final List<ToDoItem> items;
+        if(false) {
+            // the naive implementation ...
+            items = allMatches(ToDoItem.class, new Filter<ToDoItem>() {
+                @Override
+                public boolean accept(final ToDoItem t) {
+                    return ownedByCurrentUser(t) && t.isComplete();
+                }
+            });
+        } else {
+            // the JDO implementation ...
+            items = allMatches(
+                    new QueryDefault<ToDoItem>(ToDoItem.class, 
+                            "todo_complete", 
+                            "ownedBy", currentUserName()));
+        }
         if(items.isEmpty()) {
             getContainer().informUser("No to-do items have yet been completed :-(");
         }
         return items;
     }
 
-    protected List<ToDoItem> doComplete() {
-        return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
-            @Override
-            public boolean accept(final ToDoItem t) {
-                return ownedByCurrentUser(t) && t.isComplete();
-            }
-        });
-    }
 
     // //////////////////////////////////////
-    // NewToDo(action)
+    // NewToDo (action)
     // //////////////////////////////////////
 
     @MemberOrder(sequence = "3")
@@ -134,7 +151,7 @@ public class ToDoItems extends AbstractFactoryAndRepository {
     }
 
     // //////////////////////////////////////
-    // AllToDos(action)
+    // AllToDos (action)
     // //////////////////////////////////////
 
     @ActionSemantics(Of.SAFE)
@@ -156,81 +173,30 @@ public class ToDoItems extends AbstractFactoryAndRepository {
         return items;
     }
 
-    // //////////////////////////////////////
-    // priority (contributed property)
-    // //////////////////////////////////////
-    
-    @DescribedAs("The relative priority of this item compared to others (using 'due by' date)")
-    @NotInServiceMenu
-    @MemberOrder(sequence="1")
-    @ActionSemantics(Of.SAFE)
-    @NotContributed(As.ACTION)
-    @Hidden(where=Where.ALL_TABLES)
-    public Integer priority(final ToDoItem toDoItem) {
-        if(toDoItem.isComplete()) {
-            return null;
-        }
-
-        final List<ToDoItem> sortedNotYetComplete = 
-                ORDERING_DUE_BY
-                .compound(ORDERING_DESCRIPTION)
-                .sortedCopy(notYetComplete());
-        int i=1;
-        for (ToDoItem each : sortedNotYetComplete) {
-            if(each == toDoItem) {
-                return i;
-            }
-            i++;
-        }
-        return null;
-    }
 
-    private static Ordering<ToDoItem> ORDERING_DUE_BY = 
-        Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, LocalDate>(){
-            @Override
-            public LocalDate apply(ToDoItem input) {
-                return input.getDueBy();
-            }
-        });
-    
-    private static Ordering<ToDoItem> ORDERING_DESCRIPTION = 
-            Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, String>(){
-                @Override
-                public String apply(ToDoItem input) {
-                    return input.getDescription();
-                }
-            });
-    
-    // //////////////////////////////////////
-    // SimilarTo (contributed collection)
-    // //////////////////////////////////////
-    
-    @NotInServiceMenu
-    @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence="1")
-    @NotContributed(As.ACTION)
-    public List<ToDoItem> similarTo(final ToDoItem toDoItem) {
-        return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
-            @Override
-            public boolean accept(ToDoItem t) {
-                return t != toDoItem && Objects.equal(toDoItem.getCategory(), t.getCategory()) && Objects.equal(toDoItem.getOwnedBy(), t.getOwnedBy());
-            }
-        });
-    }
-    
     // //////////////////////////////////////
     // AutoComplete
     // //////////////////////////////////////
 
     @Programmatic // not part of metamodel
     public List<ToDoItem> autoComplete(final String description) {
-        return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
-            @Override
-            public boolean accept(final ToDoItem t) {
-                return ownedByCurrentUser(t) && t.getDescription().contains(description);
-            }
-
-        });
+        if(false) {
+            // the naive implementation ...
+            return allMatches(ToDoItem.class, new Filter<ToDoItem>() {
+                @Override
+                public boolean accept(final ToDoItem t) {
+                    return ownedByCurrentUser(t) && t.getDescription().contains(description);
+                }
+                
+            });
+        } else {
+            // the JDO implementation ...
+            return allMatches(
+                    new QueryDefault<ToDoItem>(ToDoItem.class, 
+                            "todo_autoComplete", 
+                            "ownedBy", currentUserName(), 
+                            "description", description));
+        }
     }
 
     // //////////////////////////////////////
@@ -273,5 +239,4 @@ public class ToDoItems extends AbstractFactoryAndRepository {
         return getContainer().getUser().getName();
     }
 
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemsJdo.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemsJdo.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemsJdo.java
deleted file mode 100644
index 1927ec0..0000000
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemsJdo.java
+++ /dev/null
@@ -1,87 +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 dom.todo;
-
-import java.util.List;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-import dom.todo.ToDoItem;
-import dom.todo.ToDoItems;
-
-import org.apache.isis.applib.query.QueryDefault;
-
-public class ToDoItemsJdo extends ToDoItems {
-
-    // {{ notYetComplete (action)
-    @Override
-    protected List<ToDoItem> doNotYetComplete() {
-        return allMatches(
-                new QueryDefault<ToDoItem>(ToDoItem.class, 
-                        "todo_notYetComplete", 
-                        "ownedBy", currentUserName()));
-    }
-    // }}
-
-    // {{ done (action)
-    @Override
-    protected List<ToDoItem> doComplete() {
-        return allMatches(
-                new QueryDefault<ToDoItem>(ToDoItem.class, 
-                        "todo_complete", 
-                        "ownedBy", currentUserName()));
-    }
-    // }}
-
-    // {{ similarTo (action)
-    @Override
-    public List<ToDoItem> similarTo(final ToDoItem thisToDoItem) {
-        final List<ToDoItem> similarToDoItems = allMatches(
-                new QueryDefault<ToDoItem>(ToDoItem.class, 
-                        "todo_similarTo", 
-                        "ownedBy", currentUserName(), 
-                        "category", thisToDoItem.getCategory()));
-        return Lists.newArrayList(Iterables.filter(similarToDoItems, excluding(thisToDoItem)));
-    }
-
-    private static Predicate<ToDoItem> excluding(final ToDoItem toDoItem) {
-        return new Predicate<ToDoItem>() {
-            @Override
-            public boolean apply(ToDoItem input) {
-                return input != toDoItem;
-            }
-        };
-    }
-    // }}
-
-    // {{ autoComplete (action)
-    @Override
-    public List<ToDoItem> autoComplete(String description) {
-        
-        return allMatches(
-                new QueryDefault<ToDoItem>(ToDoItem.class, 
-                        "todo_autoComplete", 
-                        "ownedBy", currentUserName(), 
-                        "description", description));
-    }
-    // }}
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/ToDoSystemInitializer.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/ToDoSystemInitializer.java b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/ToDoSystemInitializer.java
index 79410d9..b6d0399 100644
--- a/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/ToDoSystemInitializer.java
+++ b/example/application/quickstart_wicket_restful_jdo/integtests/src/test/java/integration/ToDoSystemInitializer.java
@@ -16,10 +16,8 @@
  */
 package integration;
 
-import dom.todo.ToDoItemsJdo;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import dom.todo.ToDoItemContributions;
+import dom.todo.ToDoItems;
 
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.config.IsisConfigurationDefault;
@@ -56,7 +54,8 @@ public class ToDoSystemInitializer {
             with(new DataNucleusPersistenceMechanismInstaller());
             
             withServices(
-                    new ToDoItemsJdo(),
+                    new ToDoItems(),
+                    new ToDoItemContributions(),
                     new WrapperFactoryDefault(),
                     new RegisterEntities(),
                     new IsisJdoSupportImpl()

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
index c24f1db..7c8b134 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/app/services/ToDoItemsFixturesService.java
@@ -22,6 +22,8 @@ import java.util.List;
 
 import com.google.common.collect.Lists;
 
+import dom.todo.ToDoItem;
+import dom.todo.ToDoItems;
 import fixture.todo.ToDoItemsFixture;
 
 import org.apache.isis.applib.AbstractService;
@@ -57,10 +59,26 @@ public class ToDoItemsFixturesService extends AbstractService {
 
     // //////////////////////////////////////
 
+    @Prototype
+    public ToDoItem installAndReturnFirst() {
+        install();
+        List<ToDoItem> notYetComplete = toDoItems.notYetComplete();
+        return !notYetComplete.isEmpty() ? notYetComplete.get(0) : null;
+    }
+
+    // //////////////////////////////////////
+
     private static void installFixturesFor(String user) {
         final FixturesInstallerDelegate installer = new FixturesInstallerDelegate().withOverride();
         installer.addFixture(new ToDoItemsFixture(user));
         installer.installFixtures();
     }
+    
+    // //////////////////////////////////////
+
+    private ToDoItems toDoItems;
+    public void injectToDoItems(ToDoItems toDoItems) {
+        this.toDoItems = toDoItems;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/07d99321/example/application/quickstart_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
index a260961..84e7bb1 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
@@ -185,7 +185,8 @@ isis.reflector.facet-decorators=org.apache.isis.core.progmodel.facetdecorators.i
 
 # if using the DataNucleus object store
 #isis.services.prefix = 
-isis.services = dom.todo.ToDoItemsJdo,\
+isis.services = dom.todo.ToDoItems,\
+                dom.todo.ToDoItemContributions,\
                 app.services.ToDoItemsFixturesService,\
                 app.services.DeveloperUtilities,\
                 org.apache.isis.core.metamodel.services.bookmarks.BookmarkServiceDefault,\