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 2016/05/20 00:44:51 UTC

[14/30] isis git commit: ISIS-1407: removed SpecificationLoaderAware.

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjector.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjector.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjector.java
new file mode 100644
index 0000000..13fe785
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjector.java
@@ -0,0 +1,514 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.metamodel.services;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
+import org.apache.isis.applib.services.publish.PublishingService;
+import org.apache.isis.core.commons.components.ApplicationScopedComponent;
+import org.apache.isis.core.commons.ensure.Assert;
+import org.apache.isis.core.commons.lang.ObjectExtensions;
+import org.apache.isis.core.commons.util.ToString;
+import org.apache.isis.core.metamodel.exceptions.MetaModelException;
+import org.apache.isis.core.metamodel.spec.InjectorMethodEvaluator;
+import org.apache.isis.core.metamodel.specloader.InjectorMethodEvaluatorDefault;
+import org.apache.isis.core.metamodel.specloader.ServiceInitializer;
+
+/**
+ * The repository of services, also able to inject into any object.
+ *
+ * <p>
+ *    Implementation is (and must be) a thread-safe.
+ * </p>
+ *
+ */
+public class ServicesInjector implements ApplicationScopedComponent {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServicesInjector.class);
+
+    //region > constructor, fields
+    /**
+     * This is mutable internally, but only ever exposed (in {@link #getRegisteredServices()}) as immutable.
+     */
+    private final List<Object> services = Lists.newArrayList();
+
+    /**
+     * If no key, not yet searched for type; otherwise the corresponding value is a {@link List} of all
+     * services that are assignable to the type.  It's possible that this is an empty list.
+     */
+    private final Map<Class<?>, List<Object>> servicesAssignableToType = Maps.newHashMap();
+
+    private final Map<Class<?>, Object> serviceByConcreteType = Maps.newHashMap();
+
+    private final InjectorMethodEvaluator injectorMethodEvaluator;
+
+    public ServicesInjector(final List<Object> services) {
+        this(services, null);
+    }
+
+    /**
+     * For testing.
+     */
+    public ServicesInjector(final List<Object> services, final InjectorMethodEvaluator injectorMethodEvaluator) {
+        this.services.addAll(services);
+        this.injectorMethodEvaluator =
+                injectorMethodEvaluator != null
+                        ? injectorMethodEvaluator
+                        : new InjectorMethodEvaluatorDefault();
+
+        autowireServicesAndContainer();
+    }
+
+    //endregion
+
+    //region > replaceServices
+
+    /**
+     * Update an individual service.
+     *
+     * <p>
+     * There should already be a service {@link #getRegisteredServices() registered} of the specified type.
+     *
+     * @return <tt>true</tt> if a service of the specified type was found and updated, <tt>false</tt> otherwise.
+     * @param existingService
+     * @param replacementService
+     */
+    public <T> void replaceService(final T existingService, final T replacementService) {
+
+        if(!services.remove(existingService)) {
+            throw new IllegalArgumentException("Service to be replaced was not found (" + existingService + ")");
+        }
+
+        services.add(replacementService);
+
+        // invalidate
+        servicesAssignableToType.clear();
+        serviceByConcreteType.clear();
+
+        autowireServicesAndContainer();
+    }
+
+    public boolean isRegisteredService(final Class<?> cls) {
+        // lazily construct cache
+        if(serviceByConcreteType.isEmpty()) {
+            for (Object service : services) {
+                final Class<?> concreteType = service.getClass();
+                serviceByConcreteType.put(concreteType, service);
+            }
+        }
+        return serviceByConcreteType.containsKey(cls);
+    }
+
+    public <T> void addFallbackIfRequired(final Class<T> serviceClass, final T serviceInstance) {
+        if(!contains(services, serviceClass)) {
+            // add to beginning;
+            // (when first introduced, this feature has been used for the
+            // FixtureScriptsDefault so that appears it top of prototyping menu; not
+            // more flexible than this currently just because of YAGNI).
+            services.add(0, serviceInstance);
+        }
+    }
+
+    /**
+     * Validate domain service Ids are unique, and that the {@link PostConstruct} method, if present, must either
+     * take no arguments or take a {@link Map} object), and that the {@link PreDestroy} method, if present, must take
+     * no arguments.
+     *
+     * <p>
+     * TODO: there seems to be some duplication/overlap with {@link ServiceInitializer}.
+     */
+    public void validateServices() {
+        validate(getRegisteredServices());
+    }
+
+    private static void validate(List<Object> serviceList) {
+        for (Object service : serviceList) {
+            final Method[] methods = service.getClass().getMethods();
+            for (Method method : methods) {
+                validatePostConstructMethods(service, method);
+                validatePreDestroyMethods(service, method);
+            }
+        }
+        ListMultimap<String, Object> servicesById = ArrayListMultimap.create();
+        for (Object service : serviceList) {
+            String id = ServiceUtil.id(service);
+            servicesById.put(id, service);
+        }
+        for (Map.Entry<String, Collection<Object>> servicesForId : servicesById.asMap().entrySet()) {
+            String serviceId = servicesForId.getKey();
+            Collection<Object> services = servicesForId.getValue();
+            if(services.size() > 1) {
+                throw new IllegalStateException(
+                        String.format("Service ids must be unique; serviceId '%s' is declared by domain services %s",
+                                serviceId, classNamesFor(services)));
+            }
+        }
+    }
+
+    private static String classNamesFor(Collection<Object> services) {
+        StringBuilder buf = new StringBuilder();
+        for (Object service : services) {
+            if(buf.length() > 0) {
+                buf.append(", ");
+            }
+            buf.append(service.getClass().getName());
+        }
+        return buf.toString();
+    }
+
+    private static void validatePostConstructMethods(Object service, Method method) {
+        final PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
+        if(postConstruct == null) {
+            return;
+        }
+        final int numParams = method.getParameterTypes().length;
+        if(numParams == 0) {
+            return;
+        }
+        if(numParams == 1 && method.getParameterTypes()[0].isAssignableFrom(Map.class)) {
+            return;
+        }
+        throw new IllegalStateException("Domain service " + service.getClass().getName() + " has @PostConstruct method " + method.getName() + "; such methods must take either no argument or 1 argument of type Map<String,String>");
+    }
+
+    private static void validatePreDestroyMethods(Object service, Method method) {
+        final PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
+        if(preDestroy == null) {
+            return;
+        }
+        final int numParams = method.getParameterTypes().length;
+        if(numParams == 0) {
+            return;
+        }
+        throw new IllegalStateException("Domain service " + service.getClass().getName() + " has @PreDestroy method " + method.getName() + "; such methods must take no arguments");
+    }
+
+
+    static boolean contains(final List<Object> services, final Class<?> serviceClass) {
+        for (Object service : services) {
+            if(serviceClass.isAssignableFrom(service.getClass())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * All registered services, as an immutable {@link List}.
+     */
+    public List<Object> getRegisteredServices() {
+        return Collections.unmodifiableList(services);
+    }
+
+    private void addServices(final List<Object> services) {
+        for (final Object service : services) {
+            if (service instanceof List) {
+                final List<Object> serviceList = ObjectExtensions.asListT(service, Object.class);
+                addServices(serviceList);
+            } else {
+                addService(service);
+            }
+        }
+    }
+
+    private boolean addService(final Object service) {
+        return services.add(service);
+    }
+
+    //endregion
+
+    //region > injectServicesInto
+
+    /**
+     * Provided by the <tt>ServicesInjector</tt> when used by framework.
+     *
+     * <p>
+     * Called in multiple places from metamodel and facets.
+     */
+    public void injectServicesInto(final Object object) {
+        Assert.assertNotNull("no services", services);
+
+        injectServices(object, Collections.unmodifiableList(services));
+    }
+
+    /**
+     * As per {@link #injectServicesInto(Object)}, but for all objects in the
+     * list.
+     */
+    public void injectServicesInto(final List<Object> objects) {
+        for (final Object object : objects) {
+            injectServicesInto(object);
+        }
+    }
+
+    //endregion
+
+    //region > injectInto
+
+    /**
+     * That is, injecting this injector...
+     */
+    public void injectInto(final Object candidate) {
+        if (ServicesInjectorAware.class.isAssignableFrom(candidate.getClass())) {
+            final ServicesInjectorAware cast = ServicesInjectorAware.class.cast(candidate);
+            cast.setServicesInjector(this);
+        }
+    }
+
+    //endregion
+
+    //region > helpers
+
+    private void injectServices(final Object object, final List<Object> services) {
+
+        final Class<?> cls = object.getClass();
+
+        autowireViaFields(object, services, cls);
+        autowireViaPrefixedMethods(object, services, cls, "set");
+        autowireViaPrefixedMethods(object, services, cls, "inject");
+    }
+
+    private void autowireViaFields(final Object object, final List<Object> services, final Class<?> cls) {
+        final List<Field> fields = Arrays.asList(cls.getDeclaredFields());
+        final Iterable<Field> injectFields = Iterables.filter(fields, new Predicate<Field>() {
+            @Override
+            public boolean apply(final Field input) {
+                final Inject annotation = input.getAnnotation(javax.inject.Inject.class);
+                return annotation != null;
+            }
+        });
+
+        for (final Field field : injectFields) {
+            autowire(object, field, services);
+        }
+
+        // recurse up the object's class hierarchy
+        final Class<?> superclass = cls.getSuperclass();
+        if(superclass != null) {
+            autowireViaFields(object, services, superclass);
+        }
+    }
+
+    private void autowire(
+            final Object object,
+            final Field field,
+            final List<Object> services) {
+
+        final Class<?> type = field.getType();
+        // don't think that type can ever be null,
+        // but Javadoc for java.lang.reflect.Field doesn't say
+        if(type == null) {
+            return;
+        }
+
+        // inject into Collection<T> or List<T>
+        if(Collection.class.isAssignableFrom(type) || List.class.isAssignableFrom(type)) {
+            final Type genericType = field.getGenericType();
+            if(genericType instanceof ParameterizedType) {
+                final ParameterizedType listParameterizedType = (ParameterizedType) genericType;
+                final Class<?> listType = (Class<?>) listParameterizedType.getActualTypeArguments()[0];
+                final List<Object> listOfServices =
+                        Collections.unmodifiableList(
+                                Lists.newArrayList(
+                                        Iterables.filter(services, new Predicate<Object>() {
+                                            @Override
+                                            public boolean apply(final Object input) {
+                                                return input != null && listType.isAssignableFrom(input.getClass());
+                                            }
+                                        })));
+                invokeInjectorField(field, object, listOfServices);
+            }
+        }
+
+        for (final Object service : services) {
+            final Class<?> serviceClass = service.getClass();
+            if(type.isAssignableFrom(serviceClass)) {
+                invokeInjectorField(field, object, service);
+                return;
+            }
+        }
+    }
+
+    private void autowireViaPrefixedMethods(
+            final Object object,
+            final List<Object> services,
+            final Class<?> cls,
+            final String prefix) {
+        final List<Method> methods = Arrays.asList(cls.getMethods());
+        final Iterable<Method> prefixedMethods = Iterables.filter(methods, new Predicate<Method>(){
+            public boolean apply(final Method method) {
+                final String methodName = method.getName();
+                return methodName.startsWith(prefix);
+            }
+        });
+
+        for (final Method prefixedMethod : prefixedMethods) {
+            autowire(object, prefixedMethod, services);
+        }
+    }
+
+    private void autowire(
+            final Object object,
+            final Method prefixedMethod,
+            final List<Object> services) {
+        for (final Object service : services) {
+            final Class<?> serviceClass = service.getClass();
+            final boolean isInjectorMethod = injectorMethodEvaluator.isInjectorMethodFor(prefixedMethod, serviceClass);
+            if(isInjectorMethod) {
+                prefixedMethod.setAccessible(true);
+                invokeInjectorMethod(prefixedMethod, object, service);
+                return;
+            }
+        }
+    }
+
+    private static void invokeMethod(final Method method, final Object target, final Object[] parameters) {
+        try {
+            method.invoke(target, parameters);
+        } catch (final SecurityException | IllegalAccessException e) {
+            throw new MetaModelException(String.format("Cannot access the %s method in %s", method.getName(), target.getClass().getName()));
+        } catch (final IllegalArgumentException e1) {
+            throw new MetaModelException(e1);
+        } catch (final InvocationTargetException e) {
+            final Throwable targetException = e.getTargetException();
+            if (targetException instanceof RuntimeException) {
+                throw (RuntimeException) targetException;
+            } else {
+                throw new MetaModelException(targetException);
+            }
+        }
+    }
+
+    private static void invokeInjectorField(final Field field, final Object target, final Object parameter) {
+        try {
+            field.setAccessible(true);
+            field.set(target, parameter);
+        } catch (final IllegalArgumentException e) {
+            throw new MetaModelException(e);
+        } catch (final IllegalAccessException e) {
+            throw new MetaModelException(String.format("Cannot access the %s field in %s", field.getName(), target.getClass().getName()));
+        }
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("injected " + parameter + " into " + new ToString(target));
+        }
+    }
+
+    private static void invokeInjectorMethod(final Method method, final Object target, final Object parameter) {
+        final Object[] parameters = new Object[] { parameter };
+        invokeMethod(method, target, parameters);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("injected " + parameter + " into " + new ToString(target));
+        }
+    }
+
+    private void autowireServicesAndContainer() {
+        injectServicesInto(this.services);
+    }
+
+
+    //endregion
+
+    //region > lookupService, lookupServices
+
+    /**
+     * Returns the first registered domain service implementing the requested type.
+     *
+     * <p>
+     * Typically there will only ever be one domain service implementing a given type,
+     * (eg {@link PublishingService}), but for some services there can be more than one
+     * (eg {@link ExceptionRecognizer}).
+     *
+     * @see #lookupServices(Class)
+     */
+    @Programmatic
+    public <T> T lookupService(final Class<T> serviceClass) {
+        final List<T> services = lookupServices(serviceClass);
+        return !services.isEmpty() ? services.get(0) : null;
+    }
+
+    /**
+     * Returns all domain services implementing the requested type, in the order
+     * that they were registered in <tt>isis.properties</tt>.
+     *
+     * <p>
+     * Typically there will only ever be one domain service implementing a given type,
+     * (eg {@link PublishingService}), but for some services there can be more than one
+     * (eg {@link ExceptionRecognizer}).
+     *
+     * @see #lookupService(Class)
+     */
+    @SuppressWarnings("unchecked")
+    @Programmatic
+    public <T> List<T> lookupServices(final Class<T> serviceClass) {
+        locateAndCache(serviceClass);
+        return Collections.unmodifiableList((List<T>) servicesAssignableToType.get(serviceClass));
+    };
+
+    private void locateAndCache(final Class<?> serviceClass) {
+        if(servicesAssignableToType.containsKey(serviceClass)) {
+            return;
+        }
+
+        final List<Object> matchingServices = Lists.newArrayList();
+        addAssignableTo(serviceClass, services, matchingServices);
+
+        servicesAssignableToType.put(serviceClass, matchingServices);
+    }
+
+    private static void addAssignableTo(final Class<?> type, final List<Object> candidates, final List<Object> filteredServicesAndContainer) {
+        final Iterable<Object> filteredServices = Iterables.filter(candidates, ofType(type));
+        filteredServicesAndContainer.addAll(Lists.newArrayList(filteredServices));
+    }
+
+    private static final Predicate<Object> ofType(final Class<?> cls) {
+        return new Predicate<Object>() {
+            @Override
+            public boolean apply(final Object input) {
+                return cls.isAssignableFrom(input.getClass());
+            }
+        };
+    }
+
+    //endregion
+
+}

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

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorDefault.java
deleted file mode 100644
index 9eb9d36..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorDefault.java
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.services;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
-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.ensure.Assert;
-import org.apache.isis.core.commons.lang.ObjectExtensions;
-import org.apache.isis.core.commons.util.ToString;
-import org.apache.isis.core.metamodel.exceptions.MetaModelException;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
-import org.apache.isis.core.metamodel.spec.InjectorMethodEvaluator;
-import org.apache.isis.core.metamodel.specloader.InjectorMethodEvaluatorDefault;
-import org.apache.isis.core.metamodel.specloader.ServiceInitializer;
-
-/**
- * Must be a thread-safe.
- */
-public class ServicesInjectorDefault implements ServicesInjectorSpi {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ServicesInjectorDefault.class);
-
-    /**
-     * This is mutable internally, but only ever exposed (in {@link #getRegisteredServices()}) as immutable.
-     */
-    private final List<Object> services = Lists.newArrayList();
-
-    /**
-     * If no key, not yet searched for type; otherwise the corresponding value is a {@link List} of all
-     * services that are assignable to the type.  It's possible that this is an empty list.
-     */
-    private final Map<Class<?>, List<Object>> servicesAssignableToType = Maps.newHashMap();
-
-    private final Map<Class<?>, Object> serviceByConcreteType = Maps.newHashMap();
-
-    private final InjectorMethodEvaluator injectorMethodEvaluator;
-
-    public ServicesInjectorDefault(final List<Object> services) {
-        this(services, null);
-    }
-
-    /**
-     * For testing.  
-     */
-    public ServicesInjectorDefault(final List<Object> services, final InjectorMethodEvaluator injectorMethodEvaluator) {
-        this.services.addAll(services);
-        this.injectorMethodEvaluator = injectorMethodEvaluator != null ? injectorMethodEvaluator : new InjectorMethodEvaluatorDefault();
-
-        autowireServicesAndContainer();
-    }
-
-    //region > replaceServices
-
-    @Override
-    public <T> void replaceService(final T existingService, final T replacementService) {
-
-        if(!services.remove(existingService)) {
-            throw new IllegalArgumentException("Service to be replaced was not found (" + existingService + ")");
-        }
-
-        services.add(replacementService);
-
-        // invalidate
-        servicesAssignableToType.clear();
-        serviceByConcreteType.clear();
-
-        autowireServicesAndContainer();
-    }
-
-    @Override
-    public boolean isRegisteredService(final Class<?> cls) {
-        // lazily construct cache
-        if(serviceByConcreteType.isEmpty()) {
-            for (Object service : services) {
-                final Class<?> concreteType = service.getClass();
-                serviceByConcreteType.put(concreteType, service);
-            }
-        }
-        return serviceByConcreteType.containsKey(cls);
-    }
-
-    @Override
-    public <T> void addFallbackIfRequired(final Class<T> serviceClass, final T serviceInstance) {
-        if(!contains(services, serviceClass)) {
-            // add to beginning;
-            // (when first introduced, this feature has been used for the
-            // FixtureScriptsDefault so that appears it top of prototyping menu; not
-            // more flexible than this currently just because of YAGNI).
-            services.add(0, serviceInstance);
-        }
-    }
-
-    /**
-     * Validate domain service Ids are unique, and that the {@link PostConstruct} method, if present, must either
-     * take no arguments or take a {@link Map} object), and that the {@link PreDestroy} method, if present, must take
-     * no arguments.
-     *
-     * <p>
-     * TODO: there seems to be some duplication/overlap with {@link ServiceInitializer}.
-     */
-    @Override
-    public void validateServices() {
-        validate(getRegisteredServices());
-    }
-
-    private static void validate(List<Object> serviceList) {
-        for (Object service : serviceList) {
-            final Method[] methods = service.getClass().getMethods();
-            for (Method method : methods) {
-                validatePostConstructMethods(service, method);
-                validatePreDestroyMethods(service, method);
-            }
-        }
-        ListMultimap<String, Object> servicesById = ArrayListMultimap.create();
-        for (Object service : serviceList) {
-            String id = ServiceUtil.id(service);
-            servicesById.put(id, service);
-        }
-        for (Map.Entry<String, Collection<Object>> servicesForId : servicesById.asMap().entrySet()) {
-            String serviceId = servicesForId.getKey();
-            Collection<Object> services = servicesForId.getValue();
-            if(services.size() > 1) {
-                throw new IllegalStateException(
-                        String.format("Service ids must be unique; serviceId '%s' is declared by domain services %s",
-                                serviceId, classNamesFor(services)));
-            }
-        }
-    }
-
-    private static String classNamesFor(Collection<Object> services) {
-        StringBuilder buf = new StringBuilder();
-        for (Object service : services) {
-            if(buf.length() > 0) {
-                buf.append(", ");
-            }
-            buf.append(service.getClass().getName());
-        }
-        return buf.toString();
-    }
-
-    private static void validatePostConstructMethods(Object service, Method method) {
-        final PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
-        if(postConstruct == null) {
-            return;
-        }
-        final int numParams = method.getParameterTypes().length;
-        if(numParams == 0) {
-            return;
-        }
-        if(numParams == 1 && method.getParameterTypes()[0].isAssignableFrom(Map.class)) {
-            return;
-        }
-        throw new IllegalStateException("Domain service " + service.getClass().getName() + " has @PostConstruct method " + method.getName() + "; such methods must take either no argument or 1 argument of type Map<String,String>");
-    }
-
-    private static void validatePreDestroyMethods(Object service, Method method) {
-        final PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
-        if(preDestroy == null) {
-            return;
-        }
-        final int numParams = method.getParameterTypes().length;
-        if(numParams == 0) {
-            return;
-        }
-        throw new IllegalStateException("Domain service " + service.getClass().getName() + " has @PreDestroy method " + method.getName() + "; such methods must take no arguments");
-    }
-
-
-    static boolean contains(final List<Object> services, final Class<?> serviceClass) {
-        for (Object service : services) {
-            if(serviceClass.isAssignableFrom(service.getClass())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-
-    @Override
-    public List<Object> getRegisteredServices() {
-        return Collections.unmodifiableList(services);
-    }
-
-    private void addServices(final List<Object> services) {
-        for (final Object service : services) {
-            if (service instanceof List) {
-                final List<Object> serviceList = ObjectExtensions.asListT(service, Object.class);
-                addServices(serviceList);
-            } else {
-                addService(service);
-            }
-        }
-    }
-
-    private boolean addService(final Object service) {
-        return services.add(service);
-    }
-
-    //endregion
-
-    //region > injectServicesInto
-
-    @Override
-    public void injectServicesInto(final Object object) {
-        Assert.assertNotNull("no services", services);
-
-        injectServices(object, Collections.unmodifiableList(services));
-    }
-
-    @Override
-    public void injectServicesInto(final List<Object> objects) {
-        for (final Object object : objects) {
-            injectServicesInto(object);
-        }
-    }
-
-    //endregion
-
-    //region > injectInto
-
-    /**
-     * That is, injecting this injector...
-     */
-    @Override
-    public void injectInto(final Object candidate) {
-        if (ServicesInjectorAware.class.isAssignableFrom(candidate.getClass())) {
-            final ServicesInjectorAware cast = ServicesInjectorAware.class.cast(candidate);
-            cast.setServicesInjector(this);
-        }
-    }
-
-    //endregion
-
-    //region > helpers
-
-    private void injectServices(final Object object, final List<Object> services) {
-
-        final Class<?> cls = object.getClass();
-
-        autowireViaFields(object, services, cls);
-        autowireViaPrefixedMethods(object, services, cls, "set");
-        autowireViaPrefixedMethods(object, services, cls, "inject");
-    }
-
-    private void autowireViaFields(final Object object, final List<Object> services, final Class<?> cls) {
-        final List<Field> fields = Arrays.asList(cls.getDeclaredFields());
-        final Iterable<Field> injectFields = Iterables.filter(fields, new Predicate<Field>() {
-            @Override
-            public boolean apply(final Field input) {
-                final Inject annotation = input.getAnnotation(javax.inject.Inject.class);
-                return annotation != null;
-            }
-        });
-
-        for (final Field field : injectFields) {
-            autowire(object, field, services);
-        }
-        
-        // recurse up the object's class hierarchy
-        final Class<?> superclass = cls.getSuperclass();
-        if(superclass != null) {
-            autowireViaFields(object, services, superclass);
-        }
-    }
-
-    private void autowire(
-            final Object object,
-            final Field field,
-            final List<Object> services) {
-
-        final Class<?> type = field.getType();
-        // don't think that type can ever be null,
-        // but Javadoc for java.lang.reflect.Field doesn't say
-        if(type == null) {
-            return;
-        }
-
-        // inject into Collection<T> or List<T>
-        if(Collection.class.isAssignableFrom(type) || List.class.isAssignableFrom(type)) {
-            final Type genericType = field.getGenericType();
-            if(genericType instanceof ParameterizedType) {
-                final ParameterizedType listParameterizedType = (ParameterizedType) genericType;
-                final Class<?> listType = (Class<?>) listParameterizedType.getActualTypeArguments()[0];
-                final List<Object> listOfServices =
-                        Collections.unmodifiableList(
-                            Lists.newArrayList(
-                                Iterables.filter(services, new Predicate<Object>() {
-                                    @Override
-                                    public boolean apply(final Object input) {
-                                        return input != null && listType.isAssignableFrom(input.getClass());
-                                    }
-                                })));
-                invokeInjectorField(field, object, listOfServices);
-            }
-        }
-
-        for (final Object service : services) {
-            final Class<?> serviceClass = service.getClass();
-            if(type.isAssignableFrom(serviceClass)) {
-                invokeInjectorField(field, object, service);
-                return;
-            }
-        }
-    }
-
-    private void autowireViaPrefixedMethods(
-            final Object object,
-            final List<Object> services,
-            final Class<?> cls,
-            final String prefix) {
-        final List<Method> methods = Arrays.asList(cls.getMethods());
-        final Iterable<Method> prefixedMethods = Iterables.filter(methods, new Predicate<Method>(){
-            public boolean apply(final Method method) {
-                final String methodName = method.getName();
-                return methodName.startsWith(prefix);
-            }
-        });
-        
-        for (final Method prefixedMethod : prefixedMethods) {
-            autowire(object, prefixedMethod, services);
-        }
-    }
-
-    private void autowire(
-            final Object object,
-            final Method prefixedMethod,
-            final List<Object> services) {
-        for (final Object service : services) {
-            final Class<?> serviceClass = service.getClass();
-            final boolean isInjectorMethod = injectorMethodEvaluator.isInjectorMethodFor(prefixedMethod, serviceClass);
-            if(isInjectorMethod) {
-                prefixedMethod.setAccessible(true);
-                invokeInjectorMethod(prefixedMethod, object, service);
-                return;
-            }
-        }
-    }
-
-    private static void invokeMethod(final Method method, final Object target, final Object[] parameters) {
-        try {
-            method.invoke(target, parameters);
-        } catch (final SecurityException | IllegalAccessException e) {
-            throw new MetaModelException(String.format("Cannot access the %s method in %s", method.getName(), target.getClass().getName()));
-        } catch (final IllegalArgumentException e1) {
-            throw new MetaModelException(e1);
-        } catch (final InvocationTargetException e) {
-            final Throwable targetException = e.getTargetException();
-            if (targetException instanceof RuntimeException) {
-                throw (RuntimeException) targetException;
-            } else {
-                throw new MetaModelException(targetException);
-            }
-        }
-    }
-
-    private static void invokeInjectorField(final Field field, final Object target, final Object parameter) {
-        try {
-            field.setAccessible(true);
-            field.set(target, parameter);
-        } catch (final IllegalArgumentException e) {
-            throw new MetaModelException(e);
-        } catch (final IllegalAccessException e) {
-            throw new MetaModelException(String.format("Cannot access the %s field in %s", field.getName(), target.getClass().getName()));
-        }
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("injected " + parameter + " into " + new ToString(target));
-        }
-    }
-
-    private static void invokeInjectorMethod(final Method method, final Object target, final Object parameter) {
-        final Object[] parameters = new Object[] { parameter };
-        invokeMethod(method, target, parameters);
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("injected " + parameter + " into " + new ToString(target));
-        }
-    }
-    
-    private void autowireServicesAndContainer() {
-        injectServicesInto(this.services);
-    }
-
-
-    //endregion
-
-    //region > lookupService, lookupServices
-
-    @Override
-    public <T> T lookupService(final Class<T> serviceClass) {
-        final List<T> services = lookupServices(serviceClass);
-        return !services.isEmpty() ? services.get(0) : null;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T> List<T> lookupServices(final Class<T> serviceClass) {
-        locateAndCache(serviceClass);
-        return Collections.unmodifiableList((List<T>) servicesAssignableToType.get(serviceClass));
-    };
-
-    private void locateAndCache(final Class<?> serviceClass) {
-        if(servicesAssignableToType.containsKey(serviceClass)) {
-           return; 
-        }
-
-        final List<Object> matchingServices = Lists.newArrayList();
-        addAssignableTo(serviceClass, services, matchingServices);
-
-        servicesAssignableToType.put(serviceClass, matchingServices);
-    }
-
-    private static void addAssignableTo(final Class<?> type, final List<Object> candidates, final List<Object> filteredServicesAndContainer) {
-        final Iterable<Object> filteredServices = Iterables.filter(candidates, ofType(type));
-        filteredServicesAndContainer.addAll(Lists.newArrayList(filteredServices));
-    }
-
-    private static final Predicate<Object> ofType(final Class<?> cls) {
-        return new Predicate<Object>() {
-            @Override
-            public boolean apply(final Object input) {
-                return cls.isAssignableFrom(input.getClass());
-            }
-        };
-    }
-
-    //endregion
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorSpi.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorSpi.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorSpi.java
deleted file mode 100644
index ef8f7e6..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/ServicesInjectorSpi.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package org.apache.isis.core.metamodel.services;
-
-import org.apache.isis.core.commons.components.ApplicationScopedComponent;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
-
-/**
- * The repository of services, also able to inject into any object.
- * 
- * <p>
- * Can be considered a mutable SPI to the {@link org.apache.isis.core.metamodel.runtimecontext.ServicesInjector} immutable API.
- */
-public interface ServicesInjectorSpi extends ApplicationScopedComponent, ServicesInjector {
-
-    /**
-     * Update an individual service.
-     *
-     * <p>
-     * There should already be a service {@link #getRegisteredServices() registered} of the specified type.
-     *
-     * @return <tt>true</tt> if a service of the specified type was found and updated, <tt>false</tt> otherwise.
-     * @param originalService
-     * @param replacementService
-     */
-    <T> void replaceService(T originalService, T replacementService);
-
-    boolean isRegisteredService(Class<?> cls);
-
-    <T> void addFallbackIfRequired(Class<T> serviceClass, T serviceInstance);
-
-    void validateServices();
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
index fd8f0c9..077a518 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
@@ -42,6 +42,7 @@ import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.fixturescripts.FixtureScript;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberType;
+import org.apache.isis.applib.services.registry.ServiceRegistry2;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.SingleIntValueFacet;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
@@ -50,23 +51,19 @@ import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemove
 import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.typicallen.TypicalLengthFacet;
 import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.metamodel.specloader.specimpl.ContributeeMember;
 
 @DomainService(
         nature = NatureOfService.DOMAIN,
         repositoryFor = ApplicationFeature.class
 )
-public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRepository, SpecificationLoaderAware,
-        ServicesInjectorAware {
+public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRepository {
 
     //region > caches
     SortedMap<ApplicationFeatureId, ApplicationFeature> packageFeatures = Maps.newTreeMap();
@@ -86,7 +83,7 @@ public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRe
     }
 
     private Collection<ObjectSpecification> primeMetaModel() {
-        final List<Object> services = servicesInjector.getRegisteredServices();
+        final List<Object> services = serviceRegistry.getRegisteredServices();
         for (final Object service : services) {
             specificationLoader.loadSpecification(service.getClass());
         }
@@ -321,7 +318,7 @@ public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRe
      * </p>
      */
     private boolean isSuperClassOfService(final ObjectSpecification spec) {
-        final List<Object> registeredServices = servicesInjector.getRegisteredServices();
+        final List<Object> registeredServices = serviceRegistry.getRegisteredServices();
 
         final Class<?> specClass = spec.getCorrespondingClass();
 
@@ -512,23 +509,15 @@ public class ApplicationFeatureRepositoryDefault implements ApplicationFeatureRe
     //endregion
 
     //region  > services (injected)
-    @Inject
-    DomainObjectContainer container;
-
-    private SpecificationLoader specificationLoader;
 
-    @Programmatic
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLoader) {
-        this.specificationLoader = specificationLoader;
-    }
+    @Inject
+    ServiceRegistry2 serviceRegistry;
 
-    private ServicesInjector servicesInjector;
+    @Inject
+    DomainObjectContainer container;
 
-    @Override
-    public void setServicesInjector(final ServicesInjector servicesInjector) {
-        this.servicesInjector = servicesInjector;
-    }
+    @Inject
+    SpecificationLoader specificationLoader;
 
     @Inject
     ApplicationFeatureFactory applicationFeatureFactory;

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/container/DomainObjectContainerDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/container/DomainObjectContainerDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/container/DomainObjectContainerDefault.java
index aee46d9..adb18a1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/container/DomainObjectContainerDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/container/DomainObjectContainerDefault.java
@@ -24,6 +24,7 @@ import java.util.Map;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
+import javax.inject.Inject;
 
 import com.google.common.base.Predicate;
 
@@ -65,13 +66,10 @@ import org.apache.isis.core.metamodel.services.container.query.QueryFindByPatter
 import org.apache.isis.core.metamodel.services.container.query.QueryFindByTitle;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 
 @DomainService(nature = NatureOfService.DOMAIN)
 public class DomainObjectContainerDefault
-        implements DomainObjectContainer,
-        PersistenceSessionServiceAware, SpecificationLoaderAware,
-        AdapterManagerAware, ExceptionRecognizer {
+        implements DomainObjectContainer, PersistenceSessionServiceAware, AdapterManagerAware, ExceptionRecognizer {
 
 
     //region > titleOf
@@ -109,7 +107,7 @@ public class DomainObjectContainerDefault
     @SuppressWarnings("unchecked")
     @Override
     public <T> T newViewModelInstance(Class<T> ofClass, String memento) {
-        final ObjectSpecification spec = getSpecificationLoader().loadSpecification(ofClass);
+        final ObjectSpecification spec = specificationLoader.loadSpecification(ofClass);
         if (!spec.containsFacet(ViewModelFacet.class)) {
             throw new IsisException("Type must be a ViewModel: " + ofClass);
         }
@@ -652,20 +650,9 @@ public class DomainObjectContainerDefault
     //region > framework dependencies
 
     private PersistenceSessionService persistenceSessionService;
-    private SpecificationLoader specificationLoader;
     private AdapterManager adapterManager;
 
 
-    protected SpecificationLoader getSpecificationLoader() {
-        return specificationLoader;
-    }
-
-    @Programmatic
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLoader) {
-        this.specificationLoader = specificationLoader;
-    }
-
 
     protected AdapterManager getAdapterManager() {
         return adapterManager;
@@ -693,31 +680,34 @@ public class DomainObjectContainerDefault
 
     //region > service dependencies
 
-    @javax.inject.Inject
+    @Inject
+    SpecificationLoader specificationLoader;
+
+    @Inject
     org.apache.isis.applib.services.config.ConfigurationService configurationService;
 
-    @javax.inject.Inject
+    @Inject
     FactoryService factoryService;
 
-    @javax.inject.Inject
+    @Inject
     MessageService messageService;
 
-    @javax.inject.Inject
+    @Inject
     RepositoryService repositoryService;
 
-    @javax.inject.Inject
+    @Inject
     ServiceRegistry serviceRegistry;
 
-    @javax.inject.Inject
+    @Inject
     TransactionService transactionService;
 
-    @javax.inject.Inject
+    @Inject
     TitleService titleService;
 
-    @javax.inject.Inject
+    @Inject
     UserService userService;
 
-    @javax.inject.Inject
+    @Inject
     WrapperFactory wrapperFactory;
 
     //endregion

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/factory/FactoryServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/factory/FactoryServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/factory/FactoryServiceDefault.java
index ebdda4f..a72d43e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/factory/FactoryServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/factory/FactoryServiceDefault.java
@@ -36,21 +36,18 @@ import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
 import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionServiceAware;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 
 @DomainService(
         nature = NatureOfService.DOMAIN
 )
-public class FactoryServiceDefault
-        implements FactoryService,
-            PersistenceSessionServiceAware, SpecificationLoaderAware {
+public class FactoryServiceDefault implements FactoryService, PersistenceSessionServiceAware {
 
 
     @Programmatic
     @Override
     @SuppressWarnings("unchecked")
     public <T> T instantiate(final Class<T> domainClass) {
-        final ObjectSpecification spec = specificationLookup.loadSpecification(domainClass);
+        final ObjectSpecification spec = specificationLoader.loadSpecification(domainClass);
         final ObjectAdapter adapter = doCreateTransientInstance(spec);
         return (T) adapter.getObject();
     }
@@ -67,7 +64,7 @@ public class FactoryServiceDefault
     @Programmatic
     @Override
     public <T> T mixin(final Class<T> mixinClass, final Object mixedIn) {
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(mixinClass);
+        final ObjectSpecification objectSpec = specificationLoader.loadSpecification(mixinClass);
         final MixinFacet mixinFacet = objectSpec.getFacet(MixinFacet.class);
         if(mixinFacet == null) {
             throw new NonRecoverableException("Class '" + mixinClass.getName() + " is not a mixin");
@@ -94,6 +91,9 @@ public class FactoryServiceDefault
     }
 
     @Inject
+    SpecificationLoader specificationLoader;
+
+    @Inject
     ServiceRegistry serviceRegistry;
 
     private PersistenceSessionService persistenceSessionService;
@@ -103,9 +103,5 @@ public class FactoryServiceDefault
         this.persistenceSessionService = persistenceSessionService;
     }
 
-    private SpecificationLoader specificationLookup;
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLookup) {
-        this.specificationLookup = specificationLookup;
-    }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
index 7904ea9..8ffb99a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
@@ -104,17 +104,15 @@ import org.apache.isis.core.metamodel.facets.properties.propertylayout.NamedFace
 import org.apache.isis.core.metamodel.facets.properties.propertylayout.RenderedAdjustedFacetForPropertyXml;
 import org.apache.isis.core.metamodel.facets.properties.propertylayout.TypicalLengthFacetForPropertyXml;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
-public abstract class GridSystemServiceAbstract<G extends Grid>
-        implements GridSystemService<G>, SpecificationLoaderAware {
+public abstract class GridSystemServiceAbstract<G extends Grid> implements GridSystemService<G> {
 
     private static final Logger LOG = LoggerFactory.getLogger(GridSystemServiceAbstract.class);
 
@@ -203,7 +201,7 @@ public abstract class GridSystemServiceAbstract<G extends Grid>
             final Class<?> domainClass) {
 
 
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+        final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainClass);
 
         final Map<String, OneToOneAssociation> oneToOneAssociationById =
                 ObjectMember.Util.mapById(getOneToOneAssociations(objectSpec));
@@ -398,7 +396,7 @@ public abstract class GridSystemServiceAbstract<G extends Grid>
     public void complete(final G grid, final Class<?> domainClass) {
         normalize(grid, domainClass);
 
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+        final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainClass);
 
         grid.visit(new Grid.VisitorAdapter() {
 
@@ -680,11 +678,8 @@ public abstract class GridSystemServiceAbstract<G extends Grid>
 
     // //////////////////////////////////////
 
-    protected SpecificationLoader specificationLookup;
-
-    public void setSpecificationLoader(final SpecificationLoader specificationLookup) {
-        this.specificationLookup = specificationLookup;
-    }
+    @Inject
+    protected SpecificationLoader specificationLoader;
 
     @Inject
     protected TranslationService translationService;

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
index 1d51fff..133cad6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
@@ -85,7 +85,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
     public Grid defaultGrid(final Class<?> domainClass) {
         final BS3Grid bs3Grid = new BS3Grid();
 
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+        final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainClass);
         bs3Grid.setDomainClass(domainClass);
 
         final BS3Row headerRow = new BS3Row();
@@ -167,7 +167,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
             final Class<?> domainClass) {
 
 
-        final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+        final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainClass);
 
         final Map<String, OneToOneAssociation> oneToOneAssociationById =
                 ObjectMember.Util.mapById(getOneToOneAssociations(objectSpec));

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
index 89dd2c9..c7ba107 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/layout/LayoutServiceDefault.java
@@ -46,13 +46,12 @@ import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 import org.apache.isis.objectstore.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
 
 @DomainService(
         nature = NatureOfService.DOMAIN
 )
-public class LayoutServiceDefault implements LayoutService, SpecificationLoaderAware {
+public class LayoutServiceDefault implements LayoutService {
 
     private static final Logger LOG = LoggerFactory.getLogger(LayoutServiceDefault.class);
 
@@ -69,7 +68,7 @@ public class LayoutServiceDefault implements LayoutService, SpecificationLoaderA
     protected Grid toGrid(final Class<?> domainClass, final Style style) {
 
         if (style == Style.CURRENT) {
-            final ObjectSpecification objectSpec = specificationLookup.loadSpecification(domainClass);
+            final ObjectSpecification objectSpec = specificationLoader.loadSpecification(domainClass);
             final GridFacet facet = objectSpec.getFacet(GridFacet.class);
             return facet != null? facet.getGrid(): null;
         }
@@ -95,7 +94,7 @@ public class LayoutServiceDefault implements LayoutService, SpecificationLoaderA
 
     @Programmatic
     public byte[] toZip(final Style style) {
-        final Collection<ObjectSpecification> allSpecs = specificationLookup.allSpecifications();
+        final Collection<ObjectSpecification> allSpecs = specificationLoader.allSpecifications();
         final Collection<ObjectSpecification> domainObjectSpecs = Collections2
                 .filter(allSpecs, new Predicate<ObjectSpecification>(){
                     @Override
@@ -138,18 +137,10 @@ public class LayoutServiceDefault implements LayoutService, SpecificationLoaderA
     }
 
 
-    ////////////////////////////////////////////////////////
 
 
-    //region > injected dependencies
-
-    private SpecificationLoader specificationLookup;
-
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLookup) {
-        this.specificationLookup = specificationLookup;
-    }
-
+    @Inject
+    SpecificationLoader specificationLoader;
 
     @Inject
     JaxbService jaxbService;

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
index 060fd07..6c1a23e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
@@ -40,18 +40,17 @@ import org.apache.isis.applib.services.metamodel.MetaModelService2;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
 @DomainService(
         nature = NatureOfService.DOMAIN
 )
-public class MetaModelServiceDefault implements MetaModelService2, SpecificationLoaderAware {
+public class MetaModelServiceDefault implements MetaModelService2 {
 
     @SuppressWarnings("unused")
     private final static Logger LOG = LoggerFactory.getLogger(MetaModelServiceDefault.class);
@@ -198,14 +197,11 @@ public class MetaModelServiceDefault implements MetaModelService2, Specification
         return sortOf(domainType);
     }
 
-    // //////////////////////////////////////
 
-    private SpecificationLoader specificationLookup;
 
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLookup) {
-        this.specificationLookup = specificationLookup;
-    }
+
+    @Inject
+    SpecificationLoader specificationLookup;
 
     @Inject
     GridService gridService;

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/registry/ServiceRegistryDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/registry/ServiceRegistryDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/registry/ServiceRegistryDefault.java
index 2f5dc95..50511f8 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/registry/ServiceRegistryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/registry/ServiceRegistryDefault.java
@@ -19,19 +19,20 @@
 
 package org.apache.isis.core.metamodel.services.registry;
 
+import java.util.List;
+
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.applib.services.registry.ServiceRegistry2;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjectorAware;
 
 @DomainService(
         nature = NatureOfService.DOMAIN
 )
-public class ServiceRegistryDefault
-        implements ServiceRegistry, ServicesInjectorAware {
+public class ServiceRegistryDefault implements ServiceRegistry2, ServicesInjectorAware {
 
 
     @Programmatic
@@ -53,6 +54,11 @@ public class ServiceRegistryDefault
         return servicesInjector.lookupServices(service);
     }
 
+    @Programmatic
+    @Override
+    public List<Object> getRegisteredServices() {
+        return servicesInjector.getRegisteredServices();
+    }
 
     private Object unwrapped(Object domainObject) {
         return wrapperFactory != null ? wrapperFactory.unwrap(domainObject) : domainObject;
@@ -66,7 +72,7 @@ public class ServiceRegistryDefault
     private ServicesInjector servicesInjector;
     @Override
     public void setServicesInjector(final ServicesInjector servicesInjector) {
-
         this.servicesInjector = servicesInjector;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
index 15dc64b..2bde668 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.services.swagger;
 import java.util.Map;
 
 import javax.annotation.PostConstruct;
+import javax.inject.Inject;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,12 +32,11 @@ import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.swagger.SwaggerService;
 import org.apache.isis.core.metamodel.services.swagger.internal.SwaggerSpecGenerator;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoaderAware;
 
 @DomainService(
         nature = NatureOfService.DOMAIN
 )
-public class SwaggerServiceDefault implements SwaggerService, SpecificationLoaderAware {
+public class SwaggerServiceDefault implements SwaggerService {
 
     @SuppressWarnings("unused")
     private final static Logger LOG = LoggerFactory.getLogger(SwaggerServiceDefault.class);
@@ -70,14 +70,8 @@ public class SwaggerServiceDefault implements SwaggerService, SpecificationLoade
         return swaggerSpec;
     }
 
-    //region > injected dependencies
-    private SpecificationLoader specificationLoader;
 
-    @Override
-    public void setSpecificationLoader(final SpecificationLoader specificationLookup) {
-        this.specificationLoader = specificationLookup;
-    }
-
-    //endregion
+    @Inject
+    SpecificationLoader specificationLoader;
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecificationDependencies.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecificationDependencies.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecificationDependencies.java
index 40275f3..bc2211f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecificationDependencies.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecificationDependencies.java
@@ -17,7 +17,7 @@
 package org.apache.isis.core.metamodel.spec;
 
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberDependencies.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberDependencies.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberDependencies.java
index 47d4223..178cf50 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberDependencies.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMemberDependencies.java
@@ -17,7 +17,7 @@
 package org.apache.isis.core.metamodel.spec.feature;
 
 import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
 public class ObjectMemberDependencies {

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index f16375a..a92cf0a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -32,9 +32,9 @@ import org.slf4j.LoggerFactory;
 import org.apache.isis.applib.AppManifest;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.core.commons.components.ApplicationScopedComponent;
 import org.apache.isis.core.commons.config.IsisConfiguration;
-import org.apache.isis.core.commons.config.IsisConfigurationDefault;
 import org.apache.isis.core.commons.ensure.Assert;
 import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.lang.ClassUtil;
@@ -46,13 +46,11 @@ import org.apache.isis.core.metamodel.facets.object.autocomplete.AutoCompleteFac
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.core.metamodel.layoutmetadata.LayoutMetadataReader;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
-import org.apache.isis.core.metamodel.runtimecontext.ConfigurationServiceInternal;
 import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
 import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
 import org.apache.isis.core.metamodel.runtimecontext.noruntime.RuntimeContextNoRuntime;
-import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjectorAware;
 import org.apache.isis.core.metamodel.spec.FreeStandingList;
 import org.apache.isis.core.metamodel.spec.ObjectSpecId;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -89,6 +87,7 @@ import static org.hamcrest.Matchers.notNullValue;
  * conjunction with some <tt>PersistenceMechanism</tt>s that do class
  * enhancement.
  * </ul>
+ * </p>
  *
  * <p>
  * In addition, the {@link RuntimeContext} can optionally be injected, but will
@@ -101,7 +100,14 @@ import static org.hamcrest.Matchers.notNullValue;
  * framework (that is, when there <i>is</i> a runtime), then the framework
  * injects an implementation of {@link RuntimeContext} that acts like a bridge
  * to its <tt>IsisContext</tt>.
- */
+ * </p>
+ *
+ * <p>
+ * Implementing class is added to {@link ServicesInjector} as an (internal) domain service; all public methods
+ * must be annotated using {@link Programmatic}.
+ * </p>
+ *
+*/
 public class SpecificationLoader implements ApplicationScopedComponent {
 
     private final static Logger LOG = LoggerFactory.getLogger(SpecificationLoader.class);
@@ -113,7 +119,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     private final ProgrammingModel programmingModel;
     private final FacetProcessor facetProcessor;
 
-    private final ServicesInjectorSpi servicesInjector;
+    private final ServicesInjector servicesInjector;
 
     private final MetaModelValidator metaModelValidator;
     private final SpecificationCacheDefault cache = new SpecificationCacheDefault();
@@ -125,7 +131,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
             final ProgrammingModel programmingModel,
             final MetaModelValidator metaModelValidator,
             final List<LayoutMetadataReader> layoutMetadataReaders,
-            final ServicesInjectorSpi servicesInjector) {
+            final ServicesInjector servicesInjector) {
 
         ensureThatArg(deploymentCategory, is(notNullValue()));
         ensureThatArg(configuration, is(notNullValue()));
@@ -140,8 +146,8 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
         this.programmingModel = programmingModel;
 
-
         this.metaModelValidator = metaModelValidator;
+
         this.facetProcessor = new FacetProcessor(configuration, programmingModel);
         this.layoutMetadataReaders = layoutMetadataReaders;
     }
@@ -170,9 +176,10 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     /**
      * Initializes and wires up, and primes the cache based on any service
-     * classes (provided by the {@link ServicesInjectorSpi}).
+     * classes (provided by the {@link ServicesInjector}).
      * @param runtimeContext
      */
+    @Programmatic
     public void init(final RuntimeContext runtimeContext) {
         this.runtimeContext = runtimeContext;
 
@@ -182,13 +189,9 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
         // default subcomponents
         if (runtimeContext == null) {
-            servicesInjector.addFallbackIfRequired(
-                    ConfigurationServiceInternal.class, new IsisConfigurationDefault(null));
             this.runtimeContext = new RuntimeContextNoRuntime(servicesInjector, this);
         }
 
-        injectInto(metaModelValidator);
-
         // wire subcomponents into each other
         this.runtimeContext.injectInto(facetProcessor);
         for (final LayoutMetadataReader layoutMetadataReader : layoutMetadataReaders) {
@@ -198,7 +201,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         // initialize subcomponents
         programmingModel.init();
         facetProcessor.init();
-        metaModelValidator.init();
+        metaModelValidator.init(this);
 
         loadSpecificationsForServices();
         loadSpecificationsForMixins();
@@ -240,6 +243,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         cache.setCacheBySpecId(specById);
     }
 
+    @Programmatic
     public boolean isInitialized() {
         return initialized;
     }
@@ -248,6 +252,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * Set using {@link #init(RuntimeContext)}.
      */
+    @Programmatic
     public RuntimeContext getRuntimeContext() {
         return runtimeContext;
     }
@@ -256,6 +261,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     //region > shutdown
 
+    @Programmatic
     public void shutdown() {
         LOG.info("shutting down " + this);
 
@@ -268,6 +274,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     //region > invalidateCache
 
+    @Programmatic
     public void invalidateCache(final Class<?> cls) {
 
         if(!cache.isInitialized()) {
@@ -305,6 +312,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     private ValidationFailures validationFailures;
 
+    @Programmatic
     public void validateAndAssert() {
         ValidationFailures validationFailures = validate();
         validationFailures.assertNone();
@@ -312,6 +320,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         cacheBySpecId();
     }
 
+    @Programmatic
     public ValidationFailures validate() {
         if(validationFailures == null) {
             validationFailures = new ValidationFailures();
@@ -333,6 +342,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
      * the configured {@link org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor}
      * has filtered out the class.
      */
+    @Programmatic
     public ObjectSpecification loadSpecification(final String className) {
         ensureThatArg(className, is(notNullValue()), "specification class name must be specified");
 
@@ -351,6 +361,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * @see #loadSpecification(String)
      */
+    @Programmatic
     public ObjectSpecification loadSpecification(final Class<?> type) {
         final ObjectSpecification spec = internalLoadSpecification(type);
         if(spec == null) {
@@ -410,6 +421,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
      * Loads the specifications of the specified types except the one specified
      * (to prevent an infinite loop).
      */
+    @Programmatic
     public boolean loadSpecifications(final List<Class<?>> typesToLoad, final Class<?> typeToIgnore) {
         boolean anyLoadedAsNull = false;
         for (final Class<?> typeToLoad : typesToLoad) {
@@ -425,6 +437,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * Loads the specifications of the specified types.
      */
+    @Programmatic
     public boolean loadSpecifications(final List<Class<?>> typesToLoad) {
         return loadSpecifications(typesToLoad, null);
     }
@@ -504,6 +517,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * Return all the loaded specifications.
      */
+    @Programmatic
     public Collection<ObjectSpecification> allSpecifications() {
         return cache.allSpecifications();
     }
@@ -512,6 +526,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
     //region > getServiceClasses, isServiceClass
 
+    @Programmatic
     public List<Class<?>> allServiceClasses() {
         List<Class<?>> serviceClasses = Lists
                 .transform(this.servicesInjector.getRegisteredServices(), new Function<Object, Class<?>>(){
@@ -523,6 +538,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         return Collections.unmodifiableList(Lists.newArrayList(serviceClasses));
     }
 
+    @Programmatic
     public boolean isServiceClass(Class<?> cls) {
         return this.servicesInjector.isRegisteredService(cls);
     }
@@ -533,6 +549,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * Whether this class has been loaded.
      */
+    @Programmatic
     public boolean loaded(final Class<?> cls) {
         return loaded(cls.getName());
     }
@@ -540,6 +557,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     /**
      * @see #loaded(Class).
      */
+    @Programmatic
     public boolean loaded(final String fullyQualifiedClassName) {
         return cache.get(fullyQualifiedClassName) != null;
     }
@@ -547,6 +565,7 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     //endregion
 
     //region > lookupBySpecId
+    @Programmatic
     public ObjectSpecification lookupBySpecId(ObjectSpecId objectSpecId) {
         final ObjectSpecification objectSpecification = cache.getByObjectType(objectSpecId);
         if(objectSpecification == null) {
@@ -563,16 +582,9 @@ public class SpecificationLoader implements ApplicationScopedComponent {
      * Injects self into candidate if required, and instructs its subcomponents
      * to do so also.
      */
+    @Programmatic
     public void injectInto(final Object candidate) {
         final Class<?> candidateClass = candidate.getClass();
-        if (SpecificationLoaderAware.class.isAssignableFrom(candidateClass)) {
-            final SpecificationLoaderAware cast = SpecificationLoaderAware.class.cast(candidate);
-            cast.setSpecificationLoader(this);
-        }
-        if (SpecificationLoaderAware.class.isAssignableFrom(candidateClass)) {
-            final SpecificationLoaderAware cast = SpecificationLoaderAware.class.cast(candidate);
-            cast.setSpecificationLoader(this);
-        }
         if (ServicesInjectorAware.class.isAssignableFrom(candidateClass)) {
             final ServicesInjectorAware cast = ServicesInjectorAware.class.cast(candidate);
             cast.setServicesInjector(this.servicesInjector);

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

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderInstaller.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderInstaller.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderInstaller.java
index cd6a86c..60a4f08 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderInstaller.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoaderInstaller.java
@@ -20,21 +20,22 @@
 package org.apache.isis.core.metamodel.specloader;
 
 import java.util.Collection;
+
 import org.apache.isis.core.commons.components.Installer;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
 import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
-import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
 
 /**
  * Installs a {@link SpecificationLoader} during system start up.
  */
 public interface SpecificationLoaderInstaller extends Installer {
 
-    static String TYPE = "reflector";
+    String TYPE = "reflector";
 
     SpecificationLoader createReflector(
             final DeploymentCategory deploymentCategory,
             final Collection<MetaModelRefiner> metaModelRefiners,
-            final ServicesInjectorSpi servicesInjector);
+            final ServicesInjector servicesInjector);
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/6e3495f4/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
index 3306a6e..bb77b40 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
@@ -55,7 +55,7 @@ import org.apache.isis.core.metamodel.interactions.InteractionUtils;
 import org.apache.isis.core.metamodel.interactions.UsabilityContext;
 import org.apache.isis.core.metamodel.interactions.VisibilityContext;
 import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;