This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
commit 272a6198a28d643a8f5ef8e0519ee6a154caecbe Author: Andi Huber <ahu...@apache.org> AuthorDate: Mon Jun 4 08:36:54 2018 +0200 ISIS-1960: Adding background error handling also init thread-pool only if required Task-Url: https://issues.apache.org/jira/browse/ISIS-1960 --- .../background/BackgroundServiceDefault.java | 222 ++++++++++++--------- .../background/ForkingInvocationHandler.java | 15 +- 2 files changed, 138 insertions(+), 99 deletions(-) diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java index 14cf750..75d20b3 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/BackgroundServiceDefault.java @@ -16,6 +16,8 @@ */ package org.apache.isis.core.runtime.services.background; +import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; + import java.lang.reflect.InvocationHandler; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -33,7 +35,6 @@ import org.apache.isis.applib.services.background.BackgroundService2; import org.apache.isis.applib.services.command.CommandContext; import org.apache.isis.applib.services.factory.FactoryService; import org.apache.isis.commons.internal._Constants; -import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.core.commons.lang.ArrayExtensions; import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal; @@ -42,92 +43,117 @@ import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.metamodel.specloader.classsubstitutor.ProxyEnhanced; import org.apache.isis.core.plugins.codegen.ProxyFactory; import org.apache.isis.core.runtime.system.session.IsisSessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Depends on an implementation of {@link org.apache.isis.applib.services.background.BackgroundCommandService} to + * For command-reification depends on an implementation of + * {@link org.apache.isis.applib.services.background.BackgroundCommandService} to * be configured. */ @DomainService( - nature = NatureOfService.DOMAIN, - menuOrder = "" + Integer.MAX_VALUE -) + nature = NatureOfService.DOMAIN, + menuOrder = "" + Integer.MAX_VALUE + ) public class BackgroundServiceDefault implements BackgroundService2 { - - private final int threadCount = Runtime.getRuntime().availableProcessors(); - private final ExecutorService backgroundExecutorService = - Executors.newFixedThreadPool(threadCount); - - @Programmatic - @PostConstruct - public void init(Map<String,String> props) { - } - - @Programmatic - @PreDestroy - public void shutdown() { - backgroundExecutorService.shutdownNow(); - } - - ObjectSpecification getSpecification(final Class<?> type) { - return specificationLoader.loadSpecification(type); - } - - // ////////////////////////////////////// - - @Programmatic - @Override - public <T> T execute(final T domainObject) { - final Class<T> cls = _Casts.uncheckedCast(domainObject.getClass()); - final InvocationHandler methodHandler = newMethodHandler(domainObject, null); - return newProxy(cls, null, methodHandler); - } - - @Override - public <T> T executeMixin(Class<T> mixinClass, Object mixedIn) { - final T mixin = factoryService.mixin(mixinClass, mixedIn); - final InvocationHandler methodHandler = newMethodHandler(mixin, mixedIn); - return newProxy(mixinClass, mixedIn, methodHandler); - } - - private <T> T newProxy( - final Class<T> cls, - final Object mixedInIfAny, - final InvocationHandler methodHandler) { - - final Class<?>[] interfaces = ArrayExtensions.combine( - cls.getInterfaces(), - new Class<?>[] { ProxyEnhanced.class }); - - final boolean initialize = mixedInIfAny!=null; - - - final Class<?>[] constructorArgTypes = initialize ? new Class<?>[] {mixedInIfAny.getClass()} : _Constants.emptyClasses; - final Object[] constructorArgs = initialize ? new Object[] {mixedInIfAny} : _Constants.emptyObjects; - - final ProxyFactory<T> proxyFactory = ProxyFactory.builder(cls) - .interfaces(interfaces) - .constructorArgTypes(constructorArgTypes) - .build(); - - return initialize - ? proxyFactory.createInstance(methodHandler, constructorArgs) - : proxyFactory.createInstance(methodHandler, false) - ; - } - - /** - * - * @param target - the object that is proxied, either a domain object or a mixin around a domain object - * @param mixedInIfAny - if target is a mixin, then this is the domain object that is mixed-in to. - */ - private <T> InvocationHandler newMethodHandler(final T target, final Object mixedInIfAny) { - - if(backgroundCommandService==null) { - return new ForkingInvocationHandler<T>(target, mixedInIfAny, backgroundExecutorService); - } - - return new CommandInvocationHandler<T>( + static final Logger LOG = LoggerFactory.getLogger(BackgroundServiceDefault.class); + + /* + * For the fixed thread-pool let there be 1-4 concurrent threads, + * limited by the number of available (logical) processor cores. + * + * Note: Future improvements might make these values configurable, + * but for now lets try to be reasonably nice here. + * + */ + private final int minThreadCount = 1; // only used if there is no BackgroundCommandService + private final int maxThreadCount = 4; // only used if there is no BackgroundCommandService + + private final int threadCount = // only used if there is no BackgroundCommandService + Math.max(minThreadCount, + Math.min(maxThreadCount, + Runtime.getRuntime().availableProcessors())); + + // only used if there is no BackgroundCommandService + private ExecutorService backgroundExecutorService; + + @Programmatic + @PostConstruct + public void init(Map<String,String> props) { + if(backgroundCommandService==null) { + backgroundExecutorService = Executors.newFixedThreadPool(threadCount); + } + } + + @Programmatic + @PreDestroy + public void shutdown() { + if(backgroundExecutorService!=null) { + backgroundExecutorService.shutdownNow(); + backgroundExecutorService = null; + } + } + + ObjectSpecification getSpecification(final Class<?> type) { + return specificationLoader.loadSpecification(type); + } + + // ////////////////////////////////////// + + @Programmatic + @Override + public <T> T execute(final T domainObject) { + final Class<T> cls = uncheckedCast(domainObject.getClass()); + final InvocationHandler methodHandler = newMethodHandler(domainObject, null); + return newProxy(cls, null, methodHandler); + } + + @Override + public <T> T executeMixin(Class<T> mixinClass, Object mixedIn) { + final T mixin = factoryService.mixin(mixinClass, mixedIn); + final InvocationHandler methodHandler = newMethodHandler(mixin, mixedIn); + return newProxy(mixinClass, mixedIn, methodHandler); + } + + private <T> T newProxy( + final Class<T> cls, + final Object mixedInIfAny, + final InvocationHandler methodHandler) { + + final Class<?>[] interfaces = ArrayExtensions.combine( + cls.getInterfaces(), + new Class<?>[] { ProxyEnhanced.class }); + + final boolean initialize = mixedInIfAny!=null; + + + final Class<?>[] constructorArgTypes = initialize ? new Class<?>[] {mixedInIfAny.getClass()} : _Constants.emptyClasses; + final Object[] constructorArgs = initialize ? new Object[] {mixedInIfAny} : _Constants.emptyObjects; + + final ProxyFactory<T> proxyFactory = ProxyFactory.builder(cls) + .interfaces(interfaces) + .constructorArgTypes(constructorArgTypes) + .build(); + + return initialize + ? proxyFactory.createInstance(methodHandler, constructorArgs) + : proxyFactory.createInstance(methodHandler, false) + ; + } + + /** + * + * @param target - the object that is proxied, either a domain object or a mixin around a domain object + * @param mixedInIfAny - if target is a mixin, then this is the domain object that is mixed-in to. + */ + private <T> InvocationHandler newMethodHandler(final T target, final Object mixedInIfAny) { + + if(backgroundCommandService==null) { + return new ForkingInvocationHandler<T>(target, mixedInIfAny, backgroundExecutorService); + } + + return new CommandInvocationHandler<T>( (BackgroundCommandService2) backgroundCommandService, target, mixedInIfAny, @@ -135,32 +161,32 @@ public class BackgroundServiceDefault implements BackgroundService2 { commandDtoServiceInternal, commandContext, this::getAdapterManager); - - } + + } - // ////////////////////////////////////// + // ////////////////////////////////////// - @javax.inject.Inject - private BackgroundCommandService backgroundCommandService; + @javax.inject.Inject + private BackgroundCommandService backgroundCommandService; - @javax.inject.Inject - private CommandDtoServiceInternal commandDtoServiceInternal; + @javax.inject.Inject + private CommandDtoServiceInternal commandDtoServiceInternal; - @javax.inject.Inject - private CommandContext commandContext; + @javax.inject.Inject + private CommandContext commandContext; - @javax.inject.Inject - private FactoryService factoryService; + @javax.inject.Inject + private FactoryService factoryService; - @javax.inject.Inject - private SpecificationLoader specificationLoader; + @javax.inject.Inject + private SpecificationLoader specificationLoader; - @javax.inject.Inject - private IsisSessionFactory isisSessionFactory; + @javax.inject.Inject + private IsisSessionFactory isisSessionFactory; - protected AdapterManager getAdapterManager() { - return isisSessionFactory.getCurrentSession().getPersistenceSession(); - } + protected AdapterManager getAdapterManager() { + return isisSessionFactory.getCurrentSession().getPersistenceSession(); + } } diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java index 9ac2593..aa4bbc3 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/background/ForkingInvocationHandler.java @@ -62,7 +62,20 @@ class ForkingInvocationHandler<T> implements InvocationHandler { } backgroundExecutorService.submit(()->{ - IsisContext.getSessionFactory().doInSession(()->proxyMethod.invoke(domainObject, args)); + + try { + + IsisContext.getSessionFactory().doInSession( + ()->proxyMethod.invoke(domainObject, args)); + + } catch (Exception e) { + // log in caller's context + BackgroundServiceDefault.LOG.error( + String.format("Background execution of action '%s' on object '%s' failed.", + proxyMethod.getName(), + domainObject.getClass().getName()), + e); + } }); return null; -- To stop receiving notification emails like this one, please contact ahu...@apache.org.