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/09/25 15:35:30 UTC

git commit: ISIS-866: @PostConstruct and @PreDestroy now honoured for request-scope services.

Repository: isis
Updated Branches:
  refs/heads/master 790e70df1 -> ad79a943c


ISIS-866: @PostConstruct and @PreDestroy now honoured for request-scope services.


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

Branch: refs/heads/master
Commit: ad79a943c51473f81b33bc0f84b17fc2819d0ea6
Parents: 790e70d
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Thu Sep 25 14:35:06 2014 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Thu Sep 25 14:35:06 2014 +0100

----------------------------------------------------------------------
 .../org/apache/isis/applib/annotation/Bulk.java |   7 +-
 .../applib/services/scratchpad/Scratchpad.java  |   3 -
 .../integtestsupport/IsisSystemForTest.java     |   3 +
 .../config/IsisConfigurationDefault.java        |  32 ++--
 .../specloader/ObjectReflectorDefault.java      |  33 +---
 .../specloader/ServiceInitializer.java          |   9 +-
 .../specloader/ServiceInitializerTest.java      |  14 +-
 .../runtime/services/ServiceInstantiator.java   | 160 ++++++++++++++++++-
 .../ServicesInstallerFromAnnotation.java        |  20 ++-
 .../ServicesInstallerFromConfiguration.java     |  24 +++
 .../system/IsisSystemFixturesHookAbstract.java  |  22 ++-
 .../services/ServiceInstantiatorTest.java       |  16 +-
 12 files changed, 249 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/applib/src/main/java/org/apache/isis/applib/annotation/Bulk.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Bulk.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Bulk.java
index 84cff20..f97790a 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Bulk.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Bulk.java
@@ -19,15 +19,10 @@
 
 package org.apache.isis.applib.annotation;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.lang.annotation.*;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-
 import javax.enterprise.context.RequestScoped;
 
 /**

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java b/core/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
index 24c84d7..7c3af84 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
@@ -19,11 +19,8 @@
 package org.apache.isis.applib.services.scratchpad;
 
 import java.util.Map;
-
 import javax.enterprise.context.RequestScoped;
-
 import com.google.common.collect.Maps;
-
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Programmatic;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
index b3a1692..08cae1e 100644
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
+++ b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
@@ -199,9 +199,12 @@ public class IsisSystemForTest implements org.junit.rules.TestRule, DomainServic
                 throw new IllegalArgumentException("Specify packagePrefixes to search for @DomainService-annotated services");
             }
             final ServicesInstallerFromAnnotation installer = new ServicesInstallerFromAnnotation();
+            installer.setConfiguration(configuration);
             installer.withPackagePrefixes(packagePrefixes);
             final List<Object> serviceList = installer.getServices(null);
             this.services.addAll(serviceList);
+
+            installer.init();
             return this;
         }
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
index a6bd278..07ff90d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
@@ -92,35 +92,35 @@ public class IsisConfigurationDefault implements IsisConfiguration {
     
     /**
      * Add the properties from an existing Properties object; if the key exists in the configuration then will be ignored.
-     * 
-     * @see #add(Properties, ContainsPolicy)
+     *
+     * @see #addPerPolicy(Properties, ContainsPolicy)
      * @see #put(Properties)
      */
     public void add(final Properties properties) {
-        add(properties, ContainsPolicy.IGNORE);
+        addPerPolicy(properties, ContainsPolicy.IGNORE);
     }
-    
+
     /**
      * Add the properties from an existing Properties object; if the key exists in the configuration then will be overwritten.
-     * 
+     *
      * @see #add(Properties)
-     * @see #add(Properties, ContainsPolicy)
+     * @see #addPerPolicy(Properties, ContainsPolicy)
      */
     public void put(final Properties properties) {
-        add(properties, ContainsPolicy.OVERWRITE);
+        addPerPolicy(properties, ContainsPolicy.OVERWRITE);
     }
-    
+
     /**
      * Add the properties from an existing Properties object; if the key exists in the configuration then the
      * {@link ContainsPolicy} will be applied.
-     * 
+     *
      * @see #add(Properties)
      * @see #put(Properties)
      */
-    public void add(final Properties properties, final ContainsPolicy policy) {
+    private void addPerPolicy(final Properties properties, final ContainsPolicy policy) {
         for(Object key: properties.keySet()) {
             Object value = properties.get(key);
-            add((String)key, (String)value);
+            addPerPolicy((String) key, (String) value, policy);
         }
     }
     
@@ -128,11 +128,11 @@ public class IsisConfigurationDefault implements IsisConfiguration {
      * Adds a key-value pair to this set of properties; if the key exists in the configuration then will be ignored.
      * 
      * <p>
-     * @see #add(String, String, ContainsPolicy)
+     * @see #addPerPolicy(String, String, ContainsPolicy)
      * @see #put(String, String)
      */
     public void add(final String key, final String value) {
-        add(key, value, ContainsPolicy.IGNORE);
+        addPerPolicy(key, value, ContainsPolicy.IGNORE);
     }
 
     /**
@@ -140,10 +140,10 @@ public class IsisConfigurationDefault implements IsisConfiguration {
      * 
      * <p>
      * @see #add(String, String)
-     * @see #add(String, String, ContainsPolicy)
+     * @see #addPerPolicy(String, String, ContainsPolicy)
      */
     public void put(final String key, final String value) {
-        add(key, value, ContainsPolicy.OVERWRITE);
+        addPerPolicy(key, value, ContainsPolicy.OVERWRITE);
     }
 
     /**
@@ -153,7 +153,7 @@ public class IsisConfigurationDefault implements IsisConfiguration {
      * @see #add(String, String)
      * @see #put(String, String)
      */
-    public void add(final String key, final String value, final ContainsPolicy policy) {
+    private void addPerPolicy(final String key, final String value, final ContainsPolicy policy) {
         if (value == null) {
             LOG.debug("ignoring " + key + " as value is null");
             return;

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
index a56b9d3..3d2400e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ObjectReflectorDefault.java
@@ -19,24 +19,12 @@
 
 package org.apache.isis.core.metamodel.specloader;
 
-import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import java.util.*;
 import com.google.common.base.Function;
 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.DomainObjectContainer;
 import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
 import org.apache.isis.core.commons.components.ApplicationScopedComponent;
@@ -60,15 +48,7 @@ import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
 import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware;
 import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
 import org.apache.isis.core.metamodel.runtimecontext.noruntime.RuntimeContextNoRuntime;
-import org.apache.isis.core.metamodel.spec.FreeStandingList;
-import org.apache.isis.core.metamodel.spec.ObjectInstantiator;
-import org.apache.isis.core.metamodel.spec.ObjectSpecId;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.SpecificationContext;
-import org.apache.isis.core.metamodel.spec.SpecificationLoader;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpiAware;
+import org.apache.isis.core.metamodel.spec.*;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext;
 import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
@@ -86,6 +66,10 @@ import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
 import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
 import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5;
 
+import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+
 /**
  * Builds the meta-model.
  * 
@@ -148,9 +132,7 @@ public final class ObjectReflectorDefault implements SpecificationLoaderSpi, App
     private final FacetProcessor facetProcessor;
 
     /**
-     * Defaulted in the constructor, so can be added to via
-     * {@link #setFacetDecorators(FacetDecoratorSet)} or
-     * {@link #addFacetDecorator(FacetDecorator)}.
+     * Initialized in the constructor.
      * 
      * <p>
      * {@link FacetDecorator}s must be added prior to {@link #init()
@@ -182,7 +164,6 @@ public final class ObjectReflectorDefault implements SpecificationLoaderSpi, App
 
     private final MetaModelValidator metaModelValidator;
     private final SpecificationCacheDefault cache = new SpecificationCacheDefault();
-    private final ServiceInitializer serviceInitializer = new ServiceInitializer();
 
     private boolean initialized = false;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ServiceInitializer.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ServiceInitializer.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ServiceInitializer.java
index 367e99e..65404ab 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ServiceInitializer.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/ServiceInitializer.java
@@ -19,16 +19,11 @@ package org.apache.isis.core.metamodel.specloader;
 import java.lang.reflect.Method;
 import java.util.List;
 import java.util.Map;
-
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
-
 import com.google.common.collect.Maps;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.lang.MethodExtensions;
 
@@ -44,7 +39,7 @@ public class ServiceInitializer {
 
     // //////////////////////////////////////
 
-    public void validate(final IsisConfiguration configuration, DomainObjectContainer container, final List<Object> services) {
+    public void validate(final IsisConfiguration configuration, final List<Object> services) {
         
         this.props = configuration.asMap();
         
@@ -111,7 +106,7 @@ public class ServiceInitializer {
         for (final Map.Entry<Object, Method> entry : postConstructMethodsByService.entrySet()) {
             final Object service = entry.getKey();
             final Method method = entry.getValue();
-            
+
             LOG.info("... calling @PostConstruct method: " + service.getClass().getName() + ": " + method.getName());
             
             final int numParams = method.getParameterTypes().length;

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/ServiceInitializerTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/ServiceInitializerTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/ServiceInitializerTest.java
index a1940a4..81c83aa 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/ServiceInitializerTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/ServiceInitializerTest.java
@@ -102,7 +102,7 @@ public class ServiceInitializerTest {
     public void postConstruct() {
         final DomainServiceWithPostConstruct d1 = new DomainServiceWithPostConstruct();
         final DomainServiceWithPostConstructWithProperties d2 = new DomainServiceWithPostConstructWithProperties();
-        serviceInitializer.validate(configuration, container, listOf(d1, d2));
+        serviceInitializer.validate(configuration, listOf(d1, d2));
         serviceInitializer.postConstruct();
         assertThat(d1.called, is(true));
         assertThat(d2.called, is(true));
@@ -122,7 +122,7 @@ public class ServiceInitializerTest {
     public void preDestroy() {
         final DomainServiceWithPreDestroy d1 = new DomainServiceWithPreDestroy();
         final DomainServiceWithPreDestroy d2 = new DomainServiceWithPreDestroy();
-        serviceInitializer.validate(configuration, container, listOf(d1, d2));
+        serviceInitializer.validate(configuration, listOf(d1, d2));
         serviceInitializer.preDestroy();
         assertThat(d1.called, is(true));
         assertThat(d2.called, is(true));
@@ -146,7 +146,7 @@ public class ServiceInitializerTest {
         expectedException.expectMessage(
                 containsString(
                 "Found more than one @PostConstruct method; service is: org.apache.isis.core.metamodel.specloader.ServiceInitializerTest$DomainServiceWithMultiplePostConstruct, found"));
-        serviceInitializer.validate(configuration, container, listOf(d1));
+        serviceInitializer.validate(configuration, listOf(d1));
     }
 
     public static class DomainServiceWithMultiplePreDestroy {
@@ -167,7 +167,7 @@ public class ServiceInitializerTest {
         expectedException.expectMessage(
                 containsString(
                 "Found more than one @PreDestroy method; service is: org.apache.isis.core.metamodel.specloader.ServiceInitializerTest$DomainServiceWithMultiplePreDestroy, found"));
-        serviceInitializer.validate(configuration, container, listOf(d1));
+        serviceInitializer.validate(configuration, listOf(d1));
     }
 
     public static class DomainServiceWithPostConstructOneArgWrongType {
@@ -183,7 +183,7 @@ public class ServiceInitializerTest {
         final DomainServiceWithPostConstructOneArgWrongType d1 = new DomainServiceWithPostConstructOneArgWrongType();
         expectedException.expectMessage(
                 "@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: org.apache.isis.core.metamodel.specloader.ServiceInitializerTest$DomainServiceWithPostConstructOneArgWrongType#y");
-        serviceInitializer.validate(configuration, container, listOf(d1));
+        serviceInitializer.validate(configuration, listOf(d1));
     }
 
     public static class DomainServiceWithPostConstructTwoArgs {
@@ -199,7 +199,7 @@ public class ServiceInitializerTest {
         final DomainServiceWithPostConstructTwoArgs d1 = new DomainServiceWithPostConstructTwoArgs();
         expectedException.expectMessage(
                 "@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: org.apache.isis.core.metamodel.specloader.ServiceInitializerTest$DomainServiceWithPostConstructTwoArgs#y");
-        serviceInitializer.validate(configuration, container, listOf(d1));
+        serviceInitializer.validate(configuration, listOf(d1));
     }
 
     public static class DomainServiceWithPreDestroyOneArgs {
@@ -216,7 +216,7 @@ public class ServiceInitializerTest {
         final DomainServiceWithPreDestroyOneArgs d1 = new DomainServiceWithPreDestroyOneArgs();
         expectedException.expectMessage(
                 "@PreDestroy method must be no-arg; method is: org.apache.isis.core.metamodel.specloader.ServiceInitializerTest$DomainServiceWithPreDestroyOneArgs#y");
-        serviceInitializer.validate(configuration, container, listOf(d1));
+        serviceInitializer.validate(configuration, listOf(d1));
     }
 
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
index f2458ad..b87c4db 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServiceInstantiator.java
@@ -19,19 +19,28 @@
 
 package org.apache.isis.core.runtime.services;
 
-import java.lang.reflect.Method;
-
 import javassist.util.proxy.MethodFilter;
 import javassist.util.proxy.MethodHandler;
 import javassist.util.proxy.ProxyFactory;
 import javassist.util.proxy.ProxyObject;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
 import javax.enterprise.context.RequestScoped;
-
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.factory.InstanceCreationClassException;
 import org.apache.isis.core.commons.factory.InstanceCreationException;
 import org.apache.isis.core.commons.lang.ArrayExtensions;
+import org.apache.isis.core.commons.lang.MethodExtensions;
 import org.apache.isis.core.metamodel.specloader.classsubstitutor.JavassistEnhanced;
 
 /**
@@ -58,12 +67,34 @@ import org.apache.isis.core.metamodel.specloader.classsubstitutor.JavassistEnhan
  */
 public final class ServiceInstantiator {
 
+    private final static Logger LOG = LoggerFactory.getLogger(ServiceInstantiator.class);
+
+
     public ServiceInstantiator() {
     }
 
     // //////////////////////////////////////
+
+    /**
+     * initially null, but checked before first use that has been set (through {@link #setConfiguration(org.apache.isis.core.commons.config.IsisConfiguration)}).
+     */
+    private Map<String, String> props;
+
+    public void setConfiguration(IsisConfiguration configuration) {
+        this.props = configuration.asMap();
+    }
+
+    private void ensureInitialized() {
+        if(props == null) {
+            throw new IllegalStateException("IsisConfiguration properties not set on ServiceInstantiator prior to first-use");
+        }
+    }
+
+
+    // //////////////////////////////////////
     
     public <T> T createInstance(final Class<T> cls) {
+        ensureInitialized();
         if(cls.isAnnotationPresent(RequestScoped.class)) {
             return instantiateRequestScopedProxy(cls);
         } else {
@@ -104,11 +135,22 @@ public final class ServiceInstantiator {
                 
                 @Override
                 public Object invoke(final Object proxied, final Method proxyMethod, final Method proxiedMethod, final Object[] args) throws Throwable {
+
+                    cacheMethodsIfNecessary(cls);
+
                     if(proxyMethod.getName().equals("__isis_startRequest")) {
                         T service = instantiate(cls);
+
+                        callPostConstructIfPresent(service);
+
                         serviceByThread.set(service);
                         return null;
                     } else if(proxyMethod.getName().equals("__isis_endRequest")) {
+
+                        final T service = serviceByThread.get();
+
+                        callPreDestroyIfPresent(service);
+
                         serviceByThread.set(null);
                         return null;
                     } else {
@@ -130,6 +172,118 @@ public final class ServiceInstantiator {
         }
     }
 
+    private Set<Class<?>> cached = Sets.newHashSet();
+    private Map<Class<?>, Method> postConstructMethodsByServiceClass = Maps.newLinkedHashMap();
+    private Map<Class<?>, Method> preDestroyMethodsByServiceClass = Maps.newLinkedHashMap();
+
+    <T> void callPostConstructIfPresent(T service) {
+
+        final Class<?> serviceClass = service.getClass();
+        final Method postConstructMethod = postConstructMethodsByServiceClass.get(serviceClass);
+        if(postConstructMethod == null) {
+            return;
+        }
+        final int numParams = postConstructMethod.getParameterTypes().length;
+
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("... calling @PostConstruct method: " + serviceClass.getName() + ": " + postConstructMethod.getName());
+        }
+        // unlike shutdown, we don't swallow exceptions; would rather fail early
+        if(numParams == 0) {
+            MethodExtensions.invoke(postConstructMethod, service);
+        } else {
+            MethodExtensions.invoke(postConstructMethod, service, new Object[]{props});
+        }
+    }
+
+    <T> void callPreDestroyIfPresent(T service) throws InvocationTargetException, IllegalAccessException {
+
+        final Class<?> serviceClass = service.getClass();
+        final Method preDestroyMethod = preDestroyMethodsByServiceClass.get(serviceClass);
+        if(preDestroyMethod == null) {
+            return;
+        }
+
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("... calling @PreDestroy method: " + serviceClass.getName() + ": " + preDestroyMethod.getName());
+        }
+        try {
+            MethodExtensions.invoke(preDestroyMethod, service);
+        } catch(Exception ex) {
+            // do nothing
+            LOG.warn("... @PreDestroy method threw exception - continuing anyway", ex);
+        }
+    }
+
+
+    private void cacheMethodsIfNecessary(Class<?> serviceClass) {
+        if(cached.contains(serviceClass)) {
+            return;
+        }
+        cacheMethods(serviceClass);
+        cached.add(serviceClass);
+    }
+
+    private void cacheMethods(Class<?> serviceClass) {
+        final Method[] methods = serviceClass.getMethods();
+
+        // @PostConstruct
+        Method postConstructMethod = null;
+        for (final Method method : methods) {
+
+            final PostConstruct postConstructAnnotation = method.getAnnotation(PostConstruct.class);
+            if (postConstructAnnotation == null) {
+                continue;
+            }
+            if (postConstructMethod != null) {
+                throw new RuntimeException("Found more than one @PostConstruct method; service is: " + serviceClass.getName() + ", found " + postConstructMethod.getName() + " and " + method.getName());
+            }
+
+            final Class<?>[] parameterTypes = method.getParameterTypes();
+            switch (parameterTypes.length) {
+                case 0:
+                    break;
+                case 1:
+                    if (Map.class != parameterTypes[0]) {
+                        throw new RuntimeException("@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: " + serviceClass.getName() + "#" + method.getName());
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: " + serviceClass.getName() + "#" + method.getName());
+            }
+            postConstructMethod = method;
+        }
+
+        // @PreDestroy
+        Method preDestroyMethod = null;
+        for (final Method method : methods) {
+
+            final PreDestroy preDestroyAnnotation = method.getAnnotation(PreDestroy.class);
+            if(preDestroyAnnotation == null) {
+                continue;
+            }
+            if(preDestroyMethod != null) {
+                throw new RuntimeException("Found more than one @PreDestroy method; service is: " + serviceClass.getName() + ", found " + preDestroyMethod.getName() + " and " + method.getName());
+            }
+
+            final Class<?>[] parameterTypes = method.getParameterTypes();
+            switch(parameterTypes.length) {
+                case 0:
+                    break;
+                default:
+                    throw new RuntimeException("@PreDestroy method must be no-arg; method is: " + serviceClass.getName() + "#" + method.getName());
+            }
+            preDestroyMethod = method;
+        }
+
+        if(postConstructMethod != null) {
+            postConstructMethodsByServiceClass.put(serviceClass, postConstructMethod);
+        }
+        if(preDestroyMethod != null) {
+            preDestroyMethodsByServiceClass.put(serviceClass, preDestroyMethod);
+        }
+    }
+
     // //////////////////////////////////////
     
     private static <T> T instantiate(final Class<T> cls) {

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/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 68c187b..e706692 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
@@ -99,16 +99,22 @@ public class ServicesInstallerFromAnnotation extends InstallerAbstract implement
         if(initialized) {
             return;
         }
-        if(packagePrefixes != null) {
-            return;
-        }
 
+        if(getConfiguration() == null) {
+            throw new IllegalStateException("No IsisConfiguration injected - aborting");
+        }
         try {
-            String packagePrefixes = getConfiguration().getString(PACKAGE_PREFIX_KEY);
-            if(Strings.isNullOrEmpty(packagePrefixes)) {
-                throw new IllegalStateException("Could not locate '" + PACKAGE_PREFIX_KEY + "' key in property files - aborting");
+
+            // lazily copy over the configuration to the instantiator
+            serviceInstantiator.setConfiguration(getConfiguration());
+
+            if(packagePrefixes == null) {
+                String packagePrefixes = getConfiguration().getString(PACKAGE_PREFIX_KEY);
+                if(Strings.isNullOrEmpty(packagePrefixes)) {
+                    throw new IllegalStateException("Could not locate '" + PACKAGE_PREFIX_KEY + "' key in property files - aborting");
+                }
+                this.packagePrefixes = packagePrefixes + PACKAGE_PREFIX_STANDARD;
             }
-            this.packagePrefixes = packagePrefixes + PACKAGE_PREFIX_STANDARD;
 
         } finally {
             initialized = true;

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/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 c8f43ec..06f530b 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
@@ -56,11 +56,35 @@ public class ServicesInstallerFromConfiguration extends InstallerAbstract implem
 
     private Map<DeploymentType, List<Object>> servicesByDeploymentType = Maps.newHashMap();
 
+    public void init() {
+        initIfRequired();
+    }
+
+    private boolean initialized = false;
+
+    protected void initIfRequired() {
+        if(initialized) {
+            return;
+        }
+
+        try {
+            // lazily copy over the configuration to the instantiator
+            serviceInstantiator.setConfiguration(getConfiguration());
+
+        } finally {
+            initialized = true;
+        }
+    }
+
+
     @Override
     public List<Object> getServices(final DeploymentType deploymentType) {
 
         LOG.info("installing " + this.getClass().getName());
 
+        // rather nasty, lazily copy over the configuration to the instantiator
+        serviceInstantiator.setConfiguration(getConfiguration());
+
         List<Object> serviceList = servicesByDeploymentType.get(deploymentType);
         if(serviceList == null) {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/runtime/src/main/java/org/apache/isis/core/runtime/system/IsisSystemFixturesHookAbstract.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/IsisSystemFixturesHookAbstract.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/IsisSystemFixturesHookAbstract.java
index a3aebc9..be43af1 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/IsisSystemFixturesHookAbstract.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/IsisSystemFixturesHookAbstract.java
@@ -54,6 +54,7 @@ import org.apache.isis.core.runtime.system.internal.SplashWindow;
 import org.apache.isis.core.runtime.system.persistence.PersistenceSessionFactory;
 import org.apache.isis.core.runtime.system.session.IsisSession;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
+import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManagerException;
 import org.apache.isis.core.runtime.userprofile.UserProfileStore;
 
@@ -171,22 +172,22 @@ public abstract class IsisSystemFixturesHookAbstract implements IsisSystem {
 
         // validate 
         final ServiceInitializer serviceInitializer = new ServiceInitializer();
-        serviceInitializer.validate(getConfiguration(), container, services);
+        serviceInitializer.validate(getConfiguration(), services);
 
         // call @PostConstruct (in a session)
         IsisContext.openSession(new InitialisationSession());
         try {
-            IsisContext.getTransactionManager().startTransaction();
+            getTransactionManager().startTransaction();
             try {
                 serviceInitializer.postConstruct();
                 
                 return serviceInitializer;
             } catch(RuntimeException ex) {
-                IsisContext.getTransactionManager().getTransaction().setAbortCause(new IsisTransactionManagerException(ex));
+                getTransactionManager().getTransaction().setAbortCause(new IsisTransactionManagerException(ex));
                 return serviceInitializer;
             } finally {
                 // will commit or abort
-                IsisContext.getTransactionManager().endTransaction();
+                getTransactionManager().endTransaction();
             }
         } finally {
             IsisContext.closeSession();
@@ -211,15 +212,15 @@ public abstract class IsisSystemFixturesHookAbstract implements IsisSystem {
         // call @PostDestroy (in a session)
         IsisContext.openSession(new InitialisationSession());
         try {
-            IsisContext.getTransactionManager().startTransaction();
+            getTransactionManager().startTransaction();
             try {
                 serviceInitializer.preDestroy();
                 
             } catch(RuntimeException ex) {
-                IsisContext.getTransactionManager().getTransaction().setAbortCause(new IsisTransactionManagerException(ex));
+                getTransactionManager().getTransaction().setAbortCause(new IsisTransactionManagerException(ex));
             } finally {
                 // will commit or abort
-                IsisContext.getTransactionManager().endTransaction();
+                getTransactionManager().endTransaction();
             }
         } finally {
             IsisContext.closeSession();
@@ -360,7 +361,7 @@ public abstract class IsisSystemFixturesHookAbstract implements IsisSystem {
     * {@link IsisConfiguration} using
     * {@link PersistenceConstants#DOMAIN_OBJECT_CONTAINER_CLASS_NAME}. If no
     * implementation is specified, then defaults to
-    * {@value PersistenceConstants#DOMAIN_OBJECT_CONTAINER_NAME_DEFAULT}.
+    * {@link PersistenceConstants#DOMAIN_OBJECT_CONTAINER_NAME_DEFAULT}.
      */
     protected DomainObjectContainer obtainContainer() {
         return createContainer(getConfiguration());
@@ -496,4 +497,9 @@ public abstract class IsisSystemFixturesHookAbstract implements IsisSystem {
         }
     }
 
+    IsisTransactionManager getTransactionManager() {
+        return IsisContext.getTransactionManager();
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/ad79a943/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
index 8d0203d..049d2d3 100644
--- a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTest.java
@@ -16,23 +16,16 @@
  */
 package org.apache.isis.core.runtime.services;
 
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.assertThat;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
-
 import javax.enterprise.context.RequestScoped;
-
-import org.joda.time.LocalDate;
 import org.junit.Before;
 import org.junit.Test;
 
-import org.apache.isis.applib.services.memento.MementoService.Memento;
-import org.apache.isis.core.commons.matchers.IsisMatchers;
-import org.apache.isis.core.runtime.services.memento.MementoServiceDefault;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
 
 public class ServiceInstantiatorTest {
 
@@ -41,6 +34,7 @@ public class ServiceInstantiatorTest {
     @Before
     public void setUp() throws Exception {
         serviceInstantiator = new ServiceInstantiator();
+        serviceInstantiator.setConfiguration(new IsisConfigurationDefault());
     }
     
     @Test