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/06/10 01:12:35 UTC

[2/3] ISIS-493: completing @DomainService auto-registration

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/DeweyOrderUtil.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/DeweyOrderUtil.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/DeweyOrderUtil.java
new file mode 100644
index 0000000..5df4e3e
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/DeweyOrderUtil.java
@@ -0,0 +1,168 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.runtime.services;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+class DeweyOrderUtil  {
+
+    public static final <V> Function<Parsed, Map.Entry<String, V>> toMapEntry() {
+        return new Function<Parsed, Map.Entry<String, V>>() {
+            @Override
+            public Map.Entry<String, V> apply(Parsed input) {
+                return input.toMapEntry();
+            }
+        };
+    }
+
+    private DeweyOrderUtil() {}
+
+    static <V> List<Map.Entry<String, V>> deweySorted(Map<String, V> map) {
+        Set<Map.Entry<String, V>> set = Sets.newLinkedHashSet();
+        for (Map.Entry<String, V> entry : map.entrySet()) {
+            set.add(entry);
+        }
+        return deweySorted(set);
+    }
+
+    static <V> List<Map.Entry<String, V>> deweySorted(Set<Map.Entry<String, V>> keys) {
+        if(keys == null) {
+            throw new IllegalArgumentException("Keys cannot be null");
+        }
+        final Iterable<Parsed<V>> parsedIter = Iterables.transform(keys,
+                new Function<Map.Entry<String,V>, Parsed<V>>() {
+                    @Override
+                    public Parsed<V> apply(Map.Entry<String,V> input) {
+                        return new Parsed(input);
+                    }
+                });
+
+        final SortedSet<Parsed<V>> parseds = Sets.newTreeSet(parsedIter);
+        final Iterable<Map.Entry<String, V>> transform = Iterables.transform(parseds, DeweyOrderUtil.<V>toMapEntry());
+        return Lists.newArrayList(transform);
+    }
+
+}
+
+
+class Parsed<V> implements Comparable<Parsed<V>> {
+    private static final Function<String, Integer> PARSE = new Function<String, Integer>() {
+        @Override
+        public Integer apply(String input) {
+            try {
+                return Integer.parseInt(input);
+            } catch (NumberFormatException e) {
+                return Integer.MAX_VALUE;
+            }
+        }
+    };
+    private final List<Integer> parts;
+    private final String key;
+    private final V value;
+    Parsed(Map.Entry<String, V> entry) {
+        key = entry.getKey();
+        value = entry.getValue();
+        final Iterable<String> iter = Splitter.on(".").split(entry.getKey());
+        parts = Lists.newArrayList(Iterators.transform(iter.iterator(),PARSE));
+    }
+
+    @Override
+    public int compareTo(Parsed<V> other) {
+        for (int i = 0; i < parts.size(); i++) {
+            Integer p = parts.get(i);
+            if (other.parts.size() < parts.size()) {
+                // run out of parts for other, put it before us
+                return -1;
+            }
+            final Integer q = other.parts.get(i);
+            final int comparison = p.compareTo(q);
+            if(comparison != 0) {
+                return +comparison;
+            }
+        }
+        if(other.parts.size() > parts.size()) {
+            // run out of parts on our side, still more on others; put us before it
+            return +1;
+        }
+        return 0;
+    }
+
+    public Map.Entry<String, V> toMapEntry() {
+        return new MapEntry<String, V>(key, value){};
+    }
+}
+
+class MapEntry<K, V> implements Map.Entry<K, V> {
+
+    private K key;
+    private V value;
+
+    MapEntry(K key, V value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    @Override
+    public K getKey() {
+        return key;
+    }
+
+    @Override
+    public V getValue() {
+        return value;
+    }
+
+    @Override
+    public V setValue(V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public boolean equals(Object object) {
+        if (object instanceof Map.Entry) {
+            Map.Entry<?, ?> that = (Map.Entry<?, ?>) object;
+            return Objects.equal(this.getKey(), that.getKey())
+                    && Objects.equal(this.getValue(), that.getValue());
+        }
+        return false;
+    }
+
+    @Override public int hashCode() {
+        K k = getKey();
+        V v = getValue();
+        return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
+    }
+
+    /**
+     * Returns a string representation of the form {@code {key}={value}}.
+     */
+    @Override public String toString() {
+        return getKey() + "=" + getValue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
index e763b1c..c12f44b 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
@@ -21,14 +21,15 @@ package org.apache.isis.core.runtime.services;
 
 import java.lang.reflect.Modifier;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedMap;
+import java.util.SortedSet;
 import javax.annotation.PreDestroy;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.reflections.Reflections;
 import org.slf4j.Logger;
@@ -47,7 +48,7 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
     public final static String PACKAGE_PREFIX_KEY = "isis.services.ServicesInstallerFromAnnotation.packagePrefix";
 
     private final ServiceInstantiator serviceInstantiator;
-    private List<Object> domainServices;
+
 
     public ServicesInstallerFromAnnotation() {
         this(new ServiceInstantiator());
@@ -63,7 +64,6 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
 
     private final SortedMap<Integer,List<Object>> positionedServices = Maps.newTreeMap();
 
-
     public void init() {
         initIfRequired();
     }
@@ -80,7 +80,6 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
                 throw new IllegalStateException("Could not locate '" + PACKAGE_PREFIX_KEY + "' key in property files - aborting");
             }
 
-            domainServices = addAllDomainServices();
         } finally {
             initialized = true;
         }
@@ -92,42 +91,6 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
 
     // //////////////////////////////////////
 
-    private List<Object> addAllDomainServices() {
-
-        final SortedMap<Integer,List<Object>> positionedServices = Maps.newTreeMap();
-
-        for (final String packagePrefix : Iterables.transform(Splitter.on(",").split(packagePrefixes), trim())) {
-            Reflections reflections = new Reflections(packagePrefix);
-
-            final Iterable<Class<?>> classes = Iterables.filter(
-                    reflections.getTypesAnnotatedWith(DomainService.class), instantiatable());
-            for (final Class<?> cls : classes) {
-
-                final DomainService domainService = cls.getAnnotation(DomainService.class);
-                final int order = domainService.value();
-                final String serviceName = cls.getName();
-
-                List<Object> list = positionedServices.get(order);
-                if(list == null) {
-                    list = Lists.newArrayList();
-                    positionedServices.put(order, list);
-                }
-
-                LOG.info("creating service " + serviceName + (order!=Integer.MAX_VALUE?" at position " + order: "" ));
-                Object service = instantiateService(cls);
-                if(service != null) {
-                    list.add(service);
-                }
-            }
-        }
-        final List<Object> serviceList = Lists.newArrayList();
-        for (Integer position : positionedServices.keySet()) {
-            final List<Object> list = positionedServices.get(position);
-            serviceList.addAll(list);
-        }
-        return serviceList;
-    }
-
     private Predicate<Class<?>> instantiatable() {
         return and(not(nullClass()), not(abstractClass()));
     }
@@ -141,7 +104,6 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
         };
     }
 
-
     private static Predicate<Class<?>> nullClass() {
         return new Predicate<Class<?>>() {
 
@@ -165,18 +127,51 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
 
     // //////////////////////////////////////
 
+    private Map<DeploymentType, List<Object>> servicesByDeploymentType = Maps.newHashMap();
+
     @Override
     public List<Object> getServices(DeploymentType deploymentType) {
         initIfRequired();
-        return domainServices;
+
+        List<Object> serviceList = servicesByDeploymentType.get(deploymentType);
+        if(serviceList == null) {
+
+            final SortedMap<String, SortedSet<String>> positionedServices = Maps.newTreeMap(new DeweyOrderComparator());
+            appendServices(deploymentType, positionedServices);
+
+            serviceList = ServicesInstallerUtils.instantiateServicesFrom(positionedServices, serviceInstantiator);
+
+            servicesByDeploymentType.put(deploymentType, serviceList);
+        }
+        return serviceList;
     }
 
     // //////////////////////////////////////
 
-    private Object instantiateService(final Class cls) {
-        return serviceInstantiator.createInstance(cls);
+    public void appendServices(
+            DeploymentType deploymentType,
+            SortedMap<String, SortedSet<String>> positionedServices) {
+        initIfRequired();
+
+        for (final String packagePrefix : Iterables.transform(Splitter.on(",").split(packagePrefixes), trim())) {
+            Reflections reflections = new Reflections(packagePrefix);
+
+            final Iterable<Class<?>> classes = Iterables.filter(
+                    reflections.getTypesAnnotatedWith(DomainService.class), instantiatable());
+            for (final Class<?> cls : classes) {
+
+                final DomainService domainService = cls.getAnnotation(DomainService.class);
+                final String order = domainService.menuOrder();
+                final String serviceName = cls.getName();
+
+                ServicesInstallerUtils.appendInPosition(positionedServices, order, serviceName);
+            }
+        }
     }
 
+
+    // //////////////////////////////////////
+
     @Override
     public List<Class<?>> getTypes() {
         return listOf(List.class); // ie List<Object.class>, of services

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfiguration.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfiguration.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfiguration.java
index 72d7325..c8f43ec 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfiguration.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfiguration.java
@@ -19,19 +19,14 @@
 
 package org.apache.isis.core.runtime.services;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.StringTokenizer;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.isis.core.commons.config.ConfigurationConstants;
 import org.apache.isis.core.commons.config.InstallerAbstract;
-import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.runtime.fixturedomainservice.ObjectFixtureService;
 import org.apache.isis.core.runtime.system.DeploymentType;
 import org.apache.isis.core.runtime.system.SystemConstants;
@@ -44,7 +39,7 @@ public class ServicesInstallerFromConfiguration extends InstallerAbstract implem
     private static final String EXPLORATION_OBJECTS = "exploration-objects";
     private static final String SERVICES_PREFIX = "services.prefix";
 
-    private static final char DELIMITER = '#';
+    private final static Pattern regex = Pattern.compile("((\\d+):)(.*)");
 
     private final ServiceInstantiator serviceInstantiator;
 
@@ -56,48 +51,54 @@ public class ServicesInstallerFromConfiguration extends InstallerAbstract implem
         super(ServicesInstaller.TYPE, "configuration");
         this.serviceInstantiator = serviceInstantiator;
     }
-    
+
+    // //////////////////////////////////////
+
+    private Map<DeploymentType, List<Object>> servicesByDeploymentType = Maps.newHashMap();
+
     @Override
     public List<Object> getServices(final DeploymentType deploymentType) {
 
         LOG.info("installing " + this.getClass().getName());
 
-        final List<Object> serviceList = Lists.newArrayList();
-        appendServices(getConfiguration(), null, serviceList);
-        appendServices(getConfiguration(), deploymentType.name(), serviceList);
+        List<Object> serviceList = servicesByDeploymentType.get(deploymentType);
+        if(serviceList == null) {
 
-        if (serviceList.size() == 0) {
-            throw new InitialisationException("No services specified");
+            final SortedMap<String, SortedSet<String>> positionedServices = Maps.newTreeMap(new DeweyOrderComparator());
+            appendServices(deploymentType, positionedServices);
+
+            serviceList = ServicesInstallerUtils.instantiateServicesFrom(positionedServices, serviceInstantiator);
+
+            servicesByDeploymentType.put(deploymentType, serviceList);
         }
-        return Collections.unmodifiableList(serviceList);
+        return serviceList;
     }
 
-    private void appendServices(final IsisConfiguration configuration, final String group, List<Object> listOfServices) {
-        final String root = ConfigurationConstants.ROOT + (group == null ? "" : group.toLowerCase() + ".");
+    // //////////////////////////////////////
 
-        String servicePrefix = configuration.getString(root + SERVICES_PREFIX);
-        if (group != null && servicePrefix == null) {
-            servicePrefix = configuration.getString(ConfigurationConstants.ROOT + SERVICES_PREFIX);
-        }
+    public void appendServices(DeploymentType deploymentType, final SortedMap<String, SortedSet<String>> positionedServices) {
 
-        final String prefix = servicePrefix(servicePrefix);
-        final String configuredServices = configuration.getString(root + SERVICES);
-        appendConfiguredServices(prefix, configuredServices, listOfServices);
-        appendObjectFixtureService(configuration, root, listOfServices);
+        appendConfiguredServices(null, positionedServices);
+        appendConfiguredServices(deploymentType, positionedServices);
+
+        appendObjectFixtureService(null, positionedServices);
     }
 
-    private final static Pattern regex = Pattern.compile("((\\d+):)(.*)");
+    private void appendConfiguredServices(final DeploymentType deploymentType, final SortedMap<String, SortedSet<String>> positionedServices) {
+        String group = deploymentType != null? deploymentType.name(): null;
+        final String root = ConfigurationConstants.ROOT + (group == null ? "" : group.toLowerCase() + ".");
 
-    private void appendConfiguredServices(final String servicePrefix, final String configuredServices, List<Object> serviceList) {
+        String servicePrefix = getConfiguration().getString(root + SERVICES_PREFIX);
+        if (group != null && servicePrefix == null) {
+            servicePrefix = getConfiguration().getString(ConfigurationConstants.ROOT + SERVICES_PREFIX);
+        }
+
+        final String configuredServices = getConfiguration().getString(root + SERVICES);
         if (configuredServices == null) {
             return;
         }
-        final SortedMap<Integer,List<Object>> positionedServices = Maps.newTreeMap();
 
         final StringTokenizer services = new StringTokenizer(configuredServices, ConfigurationConstants.LIST_SEPARATOR);
-        if (!services.hasMoreTokens()) {
-            throw new InitialisationException("Services specified, but none loaded");
-        }
         while (services.hasMoreTokens()) {
             String serviceName = services.nextToken().trim();
             if (serviceName.equals("")) {
@@ -109,57 +110,25 @@ public class ServicesInstallerFromConfiguration extends InstallerAbstract implem
                 order = Integer.parseInt(matcher.group(2));
                 serviceName = matcher.group(3);
             }
-            List<Object> list = positionedServices.get(order);
-            if(list == null) {
-                list = Lists.newArrayList();
-                positionedServices.put(order, list);
-            }
 
-            LOG.info("creating service " + serviceName + (order!=Integer.MAX_VALUE?" at position " + order: "" ));
-            Object service = instantiateService(servicePrefix, serviceName);
-            if(service != null) {
-                list.add(service);
-            }
-        }
-        for (Integer position : positionedServices.keySet()) {
-            final List<Object> list = positionedServices.get(position);
-            serviceList.addAll(list);
+            ServicesInstallerUtils.appendInPosition(positionedServices, "" + order, ServicesInstallerUtils.fullyQualifiedServiceName(servicePrefix, serviceName));
         }
     }
 
-    private Object instantiateService(final String servicePrefix, String serviceName) {
-        final int pos = serviceName.indexOf(DELIMITER);
-        if( pos == 0) {
-            // a commented out line, in other words...
-            return null;
-        }
-        if (pos != -1) {
-            final String type = serviceName.substring(0, pos);
-            if ("repository".equals(type)) {
-                final String className = servicePrefix + serviceName.substring(pos + 1);
-
-                final Class<?> underlying = loadClass(className);
-                return new SimpleRepository(underlying);
-            } else {
-                // disregard, assume the stuff after the delimiter (#) was a comment
-                final Class<?> cls = loadClass(type);
-                return serviceInstantiator.createInstance(cls);
-            }
-        }
+    private void appendObjectFixtureService(DeploymentType deploymentType, final SortedMap<String, SortedSet<String>> positionedServices) {
 
-        final Class<?> cls = loadClass(servicePrefix + serviceName);
-        return serviceInstantiator.createInstance(cls);
-    }
+        final String group = deploymentType != null? deploymentType.name(): null;
+        final String root = ConfigurationConstants.ROOT + (group == null ? "" : group.toLowerCase() + ".");
 
-    private void appendObjectFixtureService(final IsisConfiguration configuration, final String root, List<Object> serviceList) {
-        if (configuration.getBoolean(root + EXPLORATION_OBJECTS)) {
-            final DeploymentType deploymentType = DeploymentType.lookup(configuration.getString(SystemConstants.DEPLOYMENT_TYPE_KEY));
-            if (deploymentType.isExploring()) {
-                serviceList.add(new ObjectFixtureService());
+        if (getConfiguration().getBoolean(root + EXPLORATION_OBJECTS)) {
+            final DeploymentType explorationDeploymentType = DeploymentType.lookup(getConfiguration().getString(SystemConstants.DEPLOYMENT_TYPE_KEY));
+            if (explorationDeploymentType.isExploring()) {
+                ServicesInstallerUtils.appendInPosition(positionedServices, "" + Integer.MAX_VALUE, ObjectFixtureService.class.getName());
             }
         }
     }
 
+    // unused, I think....?
     private static String servicePrefix(final String servicePrefix) {
         String prefix = servicePrefix == null ? "" : servicePrefix.trim();
         if (prefix.length() > 0 && !prefix.endsWith(ConfigurationConstants.DELIMITER)) {
@@ -168,15 +137,6 @@ public class ServicesInstallerFromConfiguration extends InstallerAbstract implem
         return prefix;
     }
 
-    private static Class<?> loadClass(final String className) {
-        try {
-            LOG.debug("loading class for service: " + className);
-            return Thread.currentThread().getContextClassLoader().loadClass(className);
-        } catch (final ClassNotFoundException ex) {
-            throw new InitialisationException(String.format("Cannot find class '%s' for service", className));
-        }
-    }
-
     @Override
     public List<Class<?>> getTypes() {
         return listOf(List.class); // ie List<Object.class>, of services

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfigurationAndAnnotation.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfigurationAndAnnotation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfigurationAndAnnotation.java
index ec119bc..757f643 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfigurationAndAnnotation.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromConfigurationAndAnnotation.java
@@ -20,7 +20,10 @@
 package org.apache.isis.core.runtime.services;
 
 import java.util.List;
-import com.google.common.collect.Lists;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import com.google.common.collect.Maps;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.isis.core.commons.config.InstallerAbstract;
@@ -32,16 +35,19 @@ public class ServicesInstallerFromConfigurationAndAnnotation extends InstallerAb
 
     private static final Logger LOG = LoggerFactory.getLogger(ServicesInstallerFromConfigurationAndAnnotation.class);
 
+    private final ServiceInstantiator serviceInstantiator;
+    private final ServicesInstallerFromConfiguration servicesInstallerFromConfiguration;
+    private final ServicesInstallerFromAnnotation servicesInstallerFromAnnotation;
+
+
     public ServicesInstallerFromConfigurationAndAnnotation() {
         this(new ServiceInstantiator());
     }
 
-    private final ServicesInstallerFromConfiguration servicesInstallerFromConfiguration;
-    private final ServicesInstallerFromAnnotation servicesInstallerFromAnnotation;
-
     public ServicesInstallerFromConfigurationAndAnnotation(final ServiceInstantiator serviceInstantiator) {
         super(ServicesInstaller.TYPE, "configuration-and-annotation");
 
+        this.serviceInstantiator = serviceInstantiator;
         servicesInstallerFromConfiguration = new ServicesInstallerFromConfiguration(serviceInstantiator);
         servicesInstallerFromAnnotation = new ServicesInstallerFromAnnotation(serviceInstantiator);
     }
@@ -68,14 +74,32 @@ public class ServicesInstallerFromConfigurationAndAnnotation extends InstallerAb
         servicesInstallerFromAnnotation.shutdown();
     }
 
+    // //////////////////////////////////////
+
+    private Map<DeploymentType, List<Object>> servicesByDeploymentType = Maps.newHashMap();
+
     @Override
-    public List<Object> getServices(DeploymentType deploymentType) {
-        final List<Object> services = Lists.newArrayList();
-        services.addAll(servicesInstallerFromAnnotation.getServices(deploymentType));
-        services.addAll(servicesInstallerFromConfiguration.getServices(deploymentType));
-        return services;
+    public List<Object> getServices(final DeploymentType deploymentType) {
+
+        LOG.info("installing " + this.getClass().getName());
+
+        List<Object> serviceList = servicesByDeploymentType.get(deploymentType);
+        if(serviceList == null) {
+
+            final SortedMap<String,SortedSet<String>> positionedServices = Maps.newTreeMap(new DeweyOrderComparator());
+            servicesInstallerFromAnnotation.appendServices(deploymentType, positionedServices);
+            servicesInstallerFromConfiguration.appendServices(deploymentType, positionedServices);
+
+            serviceList = ServicesInstallerUtils.instantiateServicesFrom(positionedServices, serviceInstantiator);
+
+            servicesByDeploymentType.put(deploymentType, serviceList);
+        }
+
+        return serviceList;
     }
 
+    // //////////////////////////////////////
+
     @Override
     public List<Class<?>> getTypes() {
         return listOf(List.class); // ie List<Object.class>, of services

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerUtils.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerUtils.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerUtils.java
new file mode 100644
index 0000000..8b3e835
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerUtils.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.runtime.services;
+
+import java.util.*;
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ServicesInstallerUtils  {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServicesInstallerFromConfiguration.class);
+
+    private static final char DELIMITER = '#';
+
+    private ServicesInstallerUtils (){}
+
+    static <V extends Comparable<V>> LinkedHashSet<V> flatten(SortedMap<String, SortedSet<V>> positionedServices) {
+        final LinkedHashSet<V> serviceList = Sets.newLinkedHashSet();
+        final Set<String> keys = positionedServices.keySet();
+        for (String position : keys) {
+            final SortedSet<V> list = positionedServices.get(position);
+            serviceList.addAll(list);
+        }
+        return serviceList;
+    }
+
+    static <V> void appendInPosition(SortedMap<String, SortedSet<String>> positionedServices, String position, String service) {
+        if(service == null) {
+            return;
+        }
+        SortedSet<String> serviceList = positionedServices.get(position);
+        if(serviceList == null) {
+            serviceList = Sets.newTreeSet();
+            positionedServices.put(position, serviceList);
+        }
+        serviceList.add(service);
+    }
+
+    static Object instantiateService(String serviceName, ServiceInstantiator serviceInstantiator) {
+        final int pos = serviceName.indexOf(DELIMITER);
+        if( pos == 0) {
+            // a commented out line, in other words...
+            return null;
+        }
+        if (pos != -1) {
+            final String type = serviceName.substring(0, pos);
+            if ("repository".equals(type)) {
+                final String className = serviceName.substring(pos + 1);
+
+                final Class<?> underlying = loadClass(className);
+                return new SimpleRepository(underlying);
+            } else {
+                // disregard, assume the stuff after the delimiter (#) was a comment
+                final Class<?> cls = loadClass(type);
+                return serviceInstantiator.createInstance(cls);
+            }
+        }
+
+        final Class<?> cls = loadClass(serviceName);
+        return serviceInstantiator.createInstance(cls);
+    }
+
+    private static Class<?> loadClass(final String className) {
+        try {
+            LOG.debug("loading class for service: " + className);
+            return Thread.currentThread().getContextClassLoader().loadClass(className);
+        } catch (final ClassNotFoundException ex) {
+            throw new InitialisationException(String.format("Cannot find class '%s' for service", className));
+        }
+    }
+
+
+    static List<Object> instantiateServicesFrom(SortedMap<String, SortedSet<String>> positionedServices, final ServiceInstantiator serviceInstantiator) {
+        LinkedHashSet<String> serviceNameList = flatten(positionedServices);
+
+        final Collection<Object> filter = Collections2.filter(
+                Collections2.transform(serviceNameList, instantiator(serviceInstantiator)), Predicates.notNull());
+        return Lists.newArrayList(filter);
+    }
+
+    private static Function<String, Object> instantiator(final ServiceInstantiator serviceInstantiator) {
+        return new Function<String, Object>() {
+            @Override
+            public Object apply(String serviceName) {
+                return instantiateService(serviceName, serviceInstantiator);
+            }
+        };
+    }
+
+    static String fullyQualifiedServiceName(String servicePrefix, String serviceName) {
+        final StringBuilder buf = new StringBuilder();
+
+        if(!Strings.isNullOrEmpty(servicePrefix)) {
+            buf.append(servicePrefix);
+            if(!servicePrefix.endsWith(".")) {
+                buf.append(".");
+            }
+        }
+
+        buf.append(serviceName);
+
+        return buf.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
index 943f009..cb0084f 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java
@@ -16,23 +16,16 @@
  */
 package org.apache.isis.core.runtime.services.background;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Map;
-
 import javassist.util.proxy.MethodFilter;
 import javassist.util.proxy.MethodHandler;
 import javassist.util.proxy.ProxyFactory;
 import javassist.util.proxy.ProxyObject;
 
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
 import javax.annotation.PostConstruct;
-
 import com.google.common.collect.Lists;
-
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.background.ActionInvocationMemento;
 import org.apache.isis.applib.services.background.BackgroundCommandService;
@@ -56,6 +49,12 @@ import org.apache.isis.core.progmodel.facets.actions.invoke.CommandUtil;
 import org.apache.isis.core.runtime.services.memento.MementoServiceDefault;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 
+import static org.hamcrest.CoreMatchers.*;
+
+/**
+ * This is <i>not</i> annotated as an auto-discoverable {@link org.apache.isis.applib.annotation.DomainService} because
+ * it has mandatory dependencies.
+ */
 public class BackgroundServiceDefault implements BackgroundService {
 
     private final MementoServiceDefault mementoService;
@@ -249,7 +248,7 @@ public class BackgroundServiceDefault implements BackgroundService {
     public void injectBookmarkService(BookmarkService bookmarkService) {
         this.bookmarkService = bookmarkService;
     }
-    
+
     private BackgroundCommandService backgroundCommandService;
     /**
      * Mandatory service.

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/SystemConstants.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/SystemConstants.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/SystemConstants.java
index c42de2c..cf92c9c 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/SystemConstants.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/SystemConstants.java
@@ -32,7 +32,6 @@ import org.apache.isis.core.runtime.imageloader.TemplateImageLoaderInstaller;
 import org.apache.isis.core.runtime.installerregistry.installerapi.EmbeddedWebServerInstaller;
 import org.apache.isis.core.runtime.installerregistry.installerapi.IsisViewerInstaller;
 import org.apache.isis.core.runtime.installerregistry.installerapi.PersistenceMechanismInstaller;
-import org.apache.isis.core.runtime.installers.InstallerLookupDefault;
 import org.apache.isis.core.runtime.services.ServicesInstaller;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSessionFactory;
 import org.apache.isis.core.runtime.userprofile.UserProfileStore;
@@ -170,7 +169,7 @@ public final class SystemConstants {
      */
     public static final String IMAGE_LOADER_DEFAULT = "awt";
     /**
-     * Default if running on Google App Engine, see {@link InstallerLookupDefault#templateImageLoaderInstaller(String)}
+     * Default if running on Google App Engine, see {@link org.apache.isis.core.runtime.installerregistry.InstallerLookup#templateImageLoaderInstaller(String)}
      */
     public static final String IMAGE_LOADER_NOOP = "noop";
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
index f65bb10..794e331 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppBootstrapper.java
@@ -39,7 +39,6 @@ import org.apache.isis.core.commons.resource.ResourceStreamSourceComposite;
 import org.apache.isis.core.commons.resource.ResourceStreamSourceContextLoaderClassPath;
 import org.apache.isis.core.commons.resource.ResourceStreamSourceFileSystem;
 import org.apache.isis.core.runtime.installerregistry.InstallerLookup;
-import org.apache.isis.core.runtime.installers.InstallerLookupDefault;
 import org.apache.isis.core.runtime.logging.IsisLoggingConfigurer;
 import org.apache.isis.core.runtime.runner.IsisInjectModule;
 import org.apache.isis.core.runtime.system.DeploymentType;
@@ -111,7 +110,7 @@ public class IsisWebAppBootstrapper implements ServletContextListener {
             isisConfigurationBuilder.add(WebAppConstants.WEB_APP_DIR, webappDir);
             isisConfigurationBuilder.add(SystemConstants.NOSPLASH_KEY, "true");
 
-            final InstallerLookup installerLookup = new InstallerLookupDefault();
+            final InstallerLookup installerLookup = new InstallerLookup();
 
             injector = createGuiceInjector(isisConfigurationBuilder, deploymentType, installerLookup);
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderComparatorTest.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderComparatorTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderComparatorTest.java
new file mode 100644
index 0000000..8335632
--- /dev/null
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderComparatorTest.java
@@ -0,0 +1,88 @@
+package org.apache.isis.core.runtime.services;
+
+import java.util.*;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class DeweyOrderComparatorTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void emptySet() throws Exception {
+        assertThatSorting(
+                ofS(),
+                ofL());
+    }
+
+    @Test
+    public void singleElement() throws Exception {
+        assertThatSorting(
+                ofS("1"),
+                ofL("1")
+        );
+    }
+
+    @Test
+    public void inOrder() throws Exception {
+        assertThatSorting(
+                ofS("1", "2"),
+                ofL("1", "2")
+        );
+    }
+
+    @Test
+    public void notInOrder() throws Exception {
+        assertThatSorting(
+                ofS("2", "1"),
+                ofL("1", "2")
+        );
+    }
+
+    @Test
+    public void notInOrderDepth2() throws Exception {
+        assertThatSorting(
+                ofS("1.2", "1.1"),
+                ofL("1.1", "1.2")
+        );
+    }
+
+    @Test
+    public void differentDepths() throws Exception {
+        assertThatSorting(
+                ofS("2", "1.3", "1.2", "1.2.2", "1.2.1", "1.1"),
+                ofL("1.1", "1.2", "1.2.1", "1.2.2", "1.3", "2")
+        );
+    }
+
+    @Test
+    public void mismatchedDepth3() throws Exception {
+        assertThatSorting(
+                ofS("1.2.2", "1.2.1", "1.1"),
+                ofL("1.1", "1.2.1", "1.2.2")
+        );
+    }
+
+    private static Collection<String> ofS(String... str) {
+        return Arrays.asList(str);
+    }
+
+    private static List<String> ofL(String... str) {
+        return Lists.newArrayList(ofS(str));
+    }
+
+    private static void assertThatSorting(Collection<String> input, List<String> expected) {
+        final SortedSet<String> treeSet = new TreeSet<String>(new DeweyOrderComparator());
+        treeSet.addAll(input);
+        final List<String> strings = Arrays.asList(Iterators.toArray(treeSet.iterator(), String.class));
+        Assert.assertThat(strings, is(expected));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderUtilTest.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderUtilTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderUtilTest.java
new file mode 100644
index 0000000..671a251
--- /dev/null
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/DeweyOrderUtilTest.java
@@ -0,0 +1,95 @@
+package org.apache.isis.core.runtime.services;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class DeweyOrderUtilTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void emptySet() throws Exception {
+        assertThatSorting(
+                ofS(),
+                ofL());
+    }
+
+    @Test
+    public void singleElement() throws Exception {
+        assertThatSorting(
+                ofS(el("1", "aaa")),
+                ofL(el("1", "aaa")));
+    }
+
+    @Test
+    public void inOrder() throws Exception {
+        assertThatSorting(
+                ofS(el("1", "aaa"), el("2", "bbb")),
+                ofL(el("1", "aaa"), el("2", "bbb")));
+    }
+
+    @Test
+    public void notInOrder() throws Exception {
+        assertThatSorting(
+                ofS(el("2", "bbb"), el("1", "aaa")),
+                ofL(el("1", "aaa"), el("2", "bbb")));
+    }
+
+    @Test
+    public void notInOrderDepth2() throws Exception {
+        assertThatSorting(
+                ofS(el("1.2", "bbb"), el("1.1", "aaa")),
+                ofL(el("1.1", "aaa"), el("1.2", "bbb")));
+    }
+
+    @Test
+    public void differentDepths() throws Exception {
+        assertThatSorting(
+                ofS(el("2", "aaa"), el("1.3", "aaa"), el("1.2", "aaa"), el("1.2.2", "ccc"), el("1.2.1", "bbb"), el("1.1", "aaa")),
+                ofL(el("1.1", "aaa"), el("1.2.1", "bbb"), el("1.2.2", "ccc"), el("1.2", "aaa"), el("1.3", "aaa"), el("2", "aaa")));
+    }
+
+    @Test
+    public void mismatchedDepth3() throws Exception {
+        assertThatSorting(
+                ofS(el("1.2.2", "ccc"), el("1.2.1", "bbb"), el("1.1", "aaa")),
+                ofL(el("1.1", "aaa"), el("1.2.1", "bbb"), el("1.2.2", "ccc")));
+    }
+
+    private void assertThatSorting(Set<Map.Entry<String, Object>> input, List<Map.Entry<String, Object>> expected) {
+        final List<Map.Entry<String, Object>> list = DeweyOrderUtil.deweySorted(input);
+        Assert.assertThat(list, is(expected));
+    }
+
+
+    private static Set<Map.Entry<String, Object>> ofS(String[]... str) {
+        final Set seq = Sets.<Map.Entry<String,Object>>newLinkedHashSet();
+        for (String[] strings : str) {
+            if(strings.length != 2) {
+                throw new IllegalArgumentException("array must have 2 elements");
+            }
+            seq.add(new MapEntry<String, Object>(strings[0], (Object) strings[1]));
+        }
+        return seq;
+    }
+
+    private static List<Map.Entry<String, Object>> ofL(String[]... str) {
+        return Lists.newArrayList(ofS(str));
+    }
+
+
+    private static String[] el(String a, String b) {
+        return new String[]{a, b};
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/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 62bc688..cbc5d8b 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
@@ -20,6 +20,9 @@
 package org.apache.isis.core.wrapper;
 
 
+import org.apache.isis.applib.annotation.DomainService;
+
+@DomainService
 public class WrapperFactoryDefault extends WrapperFactoryJavassist {
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoAppDashboardService.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoAppDashboardService.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoAppDashboardService.java
index 456feb0..ad29575 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoAppDashboardService.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoAppDashboardService.java
@@ -21,9 +21,11 @@ package app;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Hidden;
 import org.apache.isis.applib.annotation.HomePage;
 
+@DomainService
 @Hidden
 public class ToDoAppDashboardService  {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysis.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysis.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysis.java
index 2e81fb8..bdc5aaf 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysis.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysis.java
@@ -30,6 +30,7 @@ import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
 
 @Named("Analysis")
+@DomainService(menuOrder = "20")
 public class ToDoItemAnalysis {
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysisContributions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysisContributions.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysisContributions.java
index 3767c00..5bdcb84 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysisContributions.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/app/ToDoItemAnalysisContributions.java
@@ -20,13 +20,11 @@ package app;
 
 import dom.todo.ToDoItem;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Hidden;
-import org.apache.isis.applib.annotation.NotContributed;
 import org.apache.isis.applib.annotation.NotContributed.As;
-import org.apache.isis.applib.annotation.NotInServiceMenu;
 
+@DomainService
 @Hidden
 public class ToDoItemAnalysisContributions {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/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
index b4678e7..001fa8a 100644
--- 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
@@ -36,6 +36,7 @@ import org.apache.isis.applib.annotation.NotContributed.As;
 import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 
+@DomainService
 public class ToDoItemContributions extends AbstractFactoryAndRepository {
 
     //region > priority (contributed property)

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
index fef2a8a..0f25420 100644
--- a/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
+++ b/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
@@ -31,6 +31,7 @@ import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
 import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
 
+@DomainService
 public class ToDoItemSubscriptions {
 
     //region > LOG

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/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 4f2e771..124f619 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
@@ -32,6 +32,7 @@ import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.clock.ClockService;
 
 @Named("ToDos")
+@DomainService(menuOrder = "10", repositoryFor = ToDoItem.class)
 public class ToDoItems {
 
     //region > identification in the UI

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixturesService.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixturesService.java b/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixturesService.java
index f8658f7..ccf2602 100644
--- a/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixturesService.java
+++ b/example/application/quickstart_wicket_restful_jdo/fixture/src/main/java/fixture/todo/ToDoItemsFixturesService.java
@@ -21,6 +21,7 @@ package fixture.todo;
 import fixture.todo.scenarios.RecreateToDoItemsAndCompleteSeveralForCurrent;
 
 import java.util.List;
+import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.Prototype;
@@ -32,13 +33,14 @@ import org.apache.isis.applib.fixturescripts.FixtureScripts;
  * Enables fixtures to be installed from the application.
  */
 @Named("Prototyping") // has the effect of defining a "Prototyping" menu item
+@DomainService(menuOrder = "40.1")
 public class ToDoItemsFixturesService extends FixtureScripts {
 
     public ToDoItemsFixturesService() {
         super("fixture.todo");
     }
 
-    //@Override // compatibility with core 1.6.0
+    //@Override // compatibility with core 1.5.0
     public FixtureScript default0RunFixtureScript() {
         return findFixtureScriptFor(RecreateToDoItemsAndCompleteSeveralForCurrent.class);
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/Admin.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/Admin.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/Admin.java
index e1811f4..f188b7b 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/Admin.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/Admin.java
@@ -30,6 +30,7 @@ import org.apache.isis.objectstore.jdo.applib.service.command.CommandServiceJdoR
 import org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo;
 import org.apache.isis.objectstore.jdo.applib.service.publish.PublishingServiceJdoRepository;
 
+@DomainService(menuOrder = "35")
 public class Admin extends AbstractService {
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/AdminContributions.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/AdminContributions.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/AdminContributions.java
index 9308108..6ab30f1 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/AdminContributions.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/admin/AdminContributions.java
@@ -25,12 +25,8 @@ import org.joda.time.LocalDate;
 
 import org.apache.isis.applib.AbstractService;
 import org.apache.isis.applib.ViewModel;
-import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Named;
-import org.apache.isis.applib.annotation.NotInServiceMenu;
-import org.apache.isis.applib.annotation.Optional;
 import org.apache.isis.applib.services.HasTransactionId;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
@@ -43,6 +39,7 @@ import org.apache.isis.objectstore.jdo.applib.service.command.CommandServiceJdoR
 import org.apache.isis.objectstore.jdo.applib.service.publish.PublishedEventJdo;
 import org.apache.isis.objectstore.jdo.applib.service.publish.PublishingServiceJdoRepository;
 
+@DomainService
 public class AdminContributions extends AbstractService {
 
     /**

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
index e1e8792..63f039d 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
@@ -18,6 +18,7 @@ package webapp.prototyping;
 
 import fixture.todo.ToDoItemsFixturesService;
 
+import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
@@ -27,6 +28,7 @@ import org.apache.isis.core.metamodel.services.devutils.DeveloperUtilitiesServic
  * These overrides are simply to 'move' the action underneath the 
  * {@link ToDoItemsFixturesService fixtures} menu.
  */
+@DomainService(menuOrder = "40.2")
 public class DeveloperUtilities extends DeveloperUtilitiesServiceDefault {
 
     @MemberOrder(name="Prototyping", sequence="90.1")

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/ExternalLinksService.java
----------------------------------------------------------------------
diff --git a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/ExternalLinksService.java b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/ExternalLinksService.java
index 79f742e..8d5d359 100644
--- a/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/ExternalLinksService.java
+++ b/example/application/quickstart_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/ExternalLinksService.java
@@ -19,12 +19,10 @@ package webapp.prototyping;
 import java.net.MalformedURLException;
 import java.net.URL;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.Named;
-import org.apache.isis.applib.annotation.Prototype;
+import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.MemberOrder;
 
+@DomainService(menuOrder = "40.3")
 public class ExternalLinksService {
 
     public static enum ExternalLink {

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/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 c254b74..217d043 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
@@ -154,44 +154,23 @@ isis.reflector.facet-decorators=org.apache.isis.core.progmodel.facetdecorators.i
 # From these domain service instances the rest of the metamodel is discovered, while the 
 # end-user gains access to other domain objects by invoking the actions of the domain services.
 #
-
+isis.services-installer=configuration-and-annotation
+isis.services.ServicesInstallerFromAnnotation.packagePrefix=app,\
+                                                            dom.todo,\
+                                                            fixture.todo,\
+                                                            webapp.admin,\
+                                                            webapp.prototyping,\
+                                                            org.apache.isis.core.wrapper,\
+                                                            org.apache.isis.applib,\
+                                                            org.apache.isis.core.metamodel.services,\
+                                                            org.apache.isis.core.runtime.services
 
 #isis.services.prefix = 
 isis.services = \
-                10:dom.todo.ToDoItems,\
-                20:app.ToDoItemAnalysis,\
-                30:webapp.admin.Admin,\
-                30:webapp.admin.AdminContributions,\
-                \
-                40:fixture.todo.ToDoItemsFixturesService,\
-                40:webapp.prototyping.DeveloperUtilities,\
-                40:webapp.prototyping.ExternalLinksService,\
-                \
-                app.ToDoAppDashboardService,\
-                app.ToDoItemAnalysisContributions,\
-                dom.todo.ToDoItemContributions,\
-                dom.todo.ToDoItemSubscriptions,\
-                \
                 # supporting services if using JDO objectstore, \
                 org.apache.isis.objectstore.jdo.applib.service.exceprecog.ExceptionRecognizerCompositeForJdoObjectStore,\
                 org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl,\
                 \
-                # Core implementation of the various bookmark/memento services, \
-                org.apache.isis.core.metamodel.services.bookmarks.BookmarkServiceDefault,\
-                org.apache.isis.core.runtime.services.memento.MementoServiceDefault,\
-                org.apache.isis.core.runtime.services.xmlsnapshot.XmlSnapshotServiceDefault,\
-                \
-                # Core implementation of a clock service, \
-                org.apache.isis.applib.services.clock.ClockService,\
-                \
-                # Core implementation of the various (inter)action services, \
-                org.apache.isis.applib.services.command.CommandContext,\
-                org.apache.isis.applib.annotation.Bulk$InteractionContext,\
-                org.apache.isis.applib.services.scratchpad.Scratchpad,\
-                org.apache.isis.applib.services.queryresultscache.QueryResultsCache,\
-                org.apache.isis.core.wrapper.WrapperFactoryDefault,\
-                org.apache.isis.applib.services.classdiscovery.ClassDiscoveryServiceUsingReflections,\
-                \
                 # JDO implementation of the EventBusService, \
                 org.apache.isis.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo,\
                 \
@@ -205,7 +184,7 @@ isis.services = \
                 org.apache.isis.objectstore.jdo.applib.service.audit.AuditingServiceJdoContributions,\
                 org.apache.isis.objectstore.jdo.applib.service.audit.AuditingServiceJdoRepository,\
                 \
-                # Core implementation of BackgroundService (depends on: MementoService & BackgroundCommandService & CommandContext service), \
+                # Core implementation of BackgroundService (depends on: MementoService & CommandContext & BackgroundCommandService), \
                 org.apache.isis.core.runtime.services.background.BackgroundServiceDefault,\
                 \
                 # JDO implementation of the BackgroundCommandService (depends on: CommandContext service), \
@@ -220,12 +199,12 @@ isis.services = \
                 org.apache.isis.viewer.restfulobjects.rendering.eventserializer.RestfulObjectsSpecEventSerializer,\
                 \
                 # JDO implementation of the two SettingsServices, \
-                #org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingsServiceJdo,\
-                #org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingsServiceJdo,\
+                # org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingsServiceJdo,\
+                # org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingsServiceJdo,\
                 \
                 # Core (example) implementations of auditing and publishing services, \
-                #org.apache.isis.applib.services.audit.AuditingService3$Stderr,\
-                #org.apache.isis.applib.services.publish.PublishingService$Stderr,\
+                # org.apache.isis.applib.services.audit.AuditingService3$Stderr,\
+                # org.apache.isis.applib.services.publish.PublishingService$Stderr,\
 
 
 # Specify the (optional) test fixtures

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/simple_wicket_restful_jdo/dom/src/main/java/dom/simple/SimpleObjects.java
----------------------------------------------------------------------
diff --git a/example/application/simple_wicket_restful_jdo/dom/src/main/java/dom/simple/SimpleObjects.java b/example/application/simple_wicket_restful_jdo/dom/src/main/java/dom/simple/SimpleObjects.java
index 6237c7f..0df6bd2 100644
--- a/example/application/simple_wicket_restful_jdo/dom/src/main/java/dom/simple/SimpleObjects.java
+++ b/example/application/simple_wicket_restful_jdo/dom/src/main/java/dom/simple/SimpleObjects.java
@@ -23,7 +23,7 @@ import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
 
-@DomainService(10)
+@DomainService(menuOrder = "2", repositoryFor = SimpleObject.class)
 public class SimpleObjects {
 
     //region > identification in the UI

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/simple_wicket_restful_jdo/fixture/src/main/java/fixture/simple/SimpleObjectsFixturesService.java
----------------------------------------------------------------------
diff --git a/example/application/simple_wicket_restful_jdo/fixture/src/main/java/fixture/simple/SimpleObjectsFixturesService.java b/example/application/simple_wicket_restful_jdo/fixture/src/main/java/fixture/simple/SimpleObjectsFixturesService.java
index 4fe76ea..bdba641 100644
--- a/example/application/simple_wicket_restful_jdo/fixture/src/main/java/fixture/simple/SimpleObjectsFixturesService.java
+++ b/example/application/simple_wicket_restful_jdo/fixture/src/main/java/fixture/simple/SimpleObjectsFixturesService.java
@@ -21,6 +21,7 @@ package fixture.simple;
 import dom.simple.SimpleObjects;
 
 import java.util.List;
+import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Named;
 import org.apache.isis.applib.annotation.Prototype;
@@ -33,13 +34,14 @@ import org.apache.isis.applib.fixturescripts.SimpleFixtureScript;
  * Enables fixtures to be installed from the application.
  */
 @Named("Prototyping")
+@DomainService(menuOrder = "1")
 public class SimpleObjectsFixturesService extends FixtureScripts {
 
     public SimpleObjectsFixturesService() {
         super("fixture.simple");
     }
 
-    //@Override // compatibility with core 1.6.0
+    //@Override // compatibility with core 1.5.0
     public FixtureScript default0RunFixtureScript() {
         return findFixtureScriptFor(SimpleFixtureScript.class);
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/simple_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
----------------------------------------------------------------------
diff --git a/example/application/simple_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java b/example/application/simple_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
index 7c66f16..91fe342 100644
--- a/example/application/simple_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
+++ b/example/application/simple_wicket_restful_jdo/webapp/src/main/java/webapp/prototyping/DeveloperUtilities.java
@@ -26,7 +26,7 @@ import org.apache.isis.core.metamodel.services.devutils.DeveloperUtilitiesServic
  * These overrides are simply to 'move' the action underneath the 
  * {@link fixture.simple.SimpleObjectsFixturesService prototyping} menu.
  */
-@DomainService(20)
+@DomainService(menuOrder = "20")
 public class DeveloperUtilities extends DeveloperUtilitiesServiceDefault {
 
     @MemberOrder(name="Prototyping", sequence="90")

http://git-wip-us.apache.org/repos/asf/isis/blob/32f16a5a/example/application/simple_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
----------------------------------------------------------------------
diff --git a/example/application/simple_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties b/example/application/simple_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
index 976c48f..829af25 100644
--- a/example/application/simple_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
+++ b/example/application/simple_wicket_restful_jdo/webapp/src/main/webapp/WEB-INF/isis.properties
@@ -40,12 +40,6 @@
 #
 #################################################################################
 
-isis.services-installer=configuration-and-annotation
-
-isis.services.ServicesInstallerFromAnnotation.packagePrefix=dom.simple,\
-                                                            webapp.prototyping,\
-                                                            org.apache.isis.applib
-
 
 #
 # configure the persistor (object store) to use
@@ -160,20 +154,21 @@ isis.reflector.facet-decorators=org.apache.isis.core.progmodel.facetdecorators.i
 # From these domain service instances the rest of the metamodel is discovered, while the 
 # end-user gains access to other domain objects by invoking the actions of the domain services.
 #
-
+isis.services-installer=configuration-and-annotation
+isis.services.ServicesInstallerFromAnnotation.packagePrefix=dom.simple,\
+                                                            fixture.simple,\
+                                                            webapp.prototyping,\
+                                                            org.apache.isis.core.wrapper,\
+                                                            org.apache.isis.applib,\
+                                                            org.apache.isis.core.metamodel.services,\
+                                                            org.apache.isis.core.runtime.services
 
 #isis.services.prefix = 
 isis.services = \
-                10:dom.simple.SimpleObjects,\
-                20:fixture.simple.SimpleObjectsFixturesService,\
-                20:webapp.prototyping.DeveloperUtilities,\
-                \
                 # supporting services if using JDO objectstore, \
                 org.apache.isis.objectstore.jdo.applib.service.exceprecog.ExceptionRecognizerCompositeForJdoObjectStore,\
                 org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl,\
                 \
-                org.apache.isis.core.wrapper.WrapperFactoryDefault,\
-                \
                 # JDO implementation of the EventBusService, \
                 org.apache.isis.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo,\
                 \
@@ -187,7 +182,7 @@ isis.services = \
                 # org.apache.isis.objectstore.jdo.applib.service.audit.AuditingServiceJdoContributions,\
                 # org.apache.isis.objectstore.jdo.applib.service.audit.AuditingServiceJdoRepository,\
                 \
-                # Core implementation of BackgroundService (depends on: MementoService & BackgroundCommandService & CommandContext service), \
+                # Core implementation of BackgroundService (depends on: MementoService & CommandContext & BackgroundCommandService), \
                 # org.apache.isis.core.runtime.services.background.BackgroundServiceDefault,\
                 \
                 # JDO implementation of the BackgroundCommandService (depends on: CommandContext service), \
@@ -202,12 +197,12 @@ isis.services = \
                 # org.apache.isis.viewer.restfulobjects.rendering.eventserializer.RestfulObjectsSpecEventSerializer,\
                 \
                 # JDO implementation of the two SettingsServices, \
-                #org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingsServiceJdo,\
-                #org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingsServiceJdo,\
+                # org.apache.isis.objectstore.jdo.applib.service.settings.ApplicationSettingsServiceJdo,\
+                # org.apache.isis.objectstore.jdo.applib.service.settings.UserSettingsServiceJdo,\
                 \
                 # Core (example) implementations of auditing and publishing services, \
-                #org.apache.isis.applib.services.audit.AuditingService3$Stderr,\
-                #org.apache.isis.applib.services.publish.PublishingService$Stderr,\
+                # org.apache.isis.applib.services.audit.AuditingService3$Stderr,\
+                # org.apache.isis.applib.services.publish.PublishingService$Stderr,\
 
 
 # Specify the (optional) test fixtures