This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch ISIS-1976-rethink-object-adapters in repository https://gitbox.apache.org/repos/asf/isis.git
commit 5412e90eee290c44b96f35e7a3f7b1392788c2c6 Author: Andi Huber <ahu...@apache.org> AuthorDate: Thu Sep 6 06:04:41 2018 +0200 ISIS-1976: add layer of abstraction Task-Url: https://issues.apache.org/jira/browse/ISIS-1976 --- .../core/metamodel/services/ServicesInjector.java | 39 +++-- .../adaptermanager/ObjectAdapterContext.java | 194 +++++++++++---------- .../ObjectAdapterContext_AdapterManager.java | 3 +- ...ObjectAdapterContext_ObjectAdapterProvider.java | 12 +- 4 files changed, 140 insertions(+), 108 deletions(-) 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 index f80535b..d293abc 100644 --- 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 @@ -23,11 +23,16 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; +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.commons.internal.base._Lazy; import org.apache.isis.commons.internal.base._NullSafe; import org.apache.isis.commons.internal.collections._Collections; import org.apache.isis.commons.internal.collections._Lists; @@ -48,8 +53,6 @@ import org.apache.isis.core.metamodel.specloader.InjectorMethodEvaluatorDefault; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.runtime.authentication.AuthenticationManager; import org.apache.isis.core.runtime.authorization.AuthorizationManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The repository of services, also able to inject into any object. @@ -78,7 +81,7 @@ public class ServicesInjector implements ApplicationScopedComponent { * 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 _Lazy<Map<Class<?>, Object>> serviceByConcreteType = _Lazy.of(this::initServiceByConcreteType); private final Map<Class<?>, Method[]> methodsByClassCache = _Maps.newHashMap(); private final Map<Class<?>, Field[]> fieldsByClassCache = _Maps.newHashMap(); @@ -143,18 +146,30 @@ public class ServicesInjector implements ApplicationScopedComponent { serviceByConcreteType.clear(); autowire(); } + + private Map<Class<?>, Object> initServiceByConcreteType(){ + final Map<Class<?>, Object> map = _Maps.newHashMap(); + for (Object service : services) { + final Class<?> concreteType = service.getClass(); + map.put(concreteType, service); + } + return map; + } 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); + return serviceByConcreteType.get().containsKey(cls); } + public boolean isRegisteredServiceInstance(final Object pojo) { + if(pojo==null) { + return false; + } + final Class<?> key = pojo.getClass(); + final Object serviceInstance = serviceByConcreteType.get().get(key); + return Objects.equals(pojo, serviceInstance); + } + + public <T> void addFallbackIfRequired(final Class<T> serviceClass, final T serviceInstance) { if(!contains(services, serviceClass)) { // add to beginning; @@ -206,7 +221,7 @@ public class ServicesInjector implements ApplicationScopedComponent { public List<Object> getRegisteredServices() { return Collections.unmodifiableList(services); } - + // -- INJECT SERVICES INTO /** diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java index 5ebfe1f..06642b2 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java @@ -22,10 +22,14 @@ import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.cache.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.commons.internal.functions._Predicates; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.ensure.Ensure; @@ -44,7 +48,9 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.runtime.memento.Data; +import org.apache.isis.core.runtime.persistence.adapter.PojoAdapter; import org.apache.isis.core.runtime.system.persistence.PersistenceSession; +import org.apache.isis.core.runtime.threadpool.ThreadPoolSupport; /** * Encapsulate ObjectAdpater life-cycling. @@ -67,9 +73,76 @@ public class ObjectAdapterContext { return objectAdapterContext; } - private final PojoAdapterHashMap pojoAdapterMap = new PojoAdapterHashMap(); - private final OidAdapterHashMap oidAdapterMap = new OidAdapterHashMap(); - private final OidAdapterHashMap oidAdapterMap2 = new OidAdapterHashMap(); + private final static class Cache { + private final PojoAdapterHashMap pojoAdapterMap = new PojoAdapterHashMap(); + private final OidAdapterHashMap oidAdapterMap = new OidAdapterHashMap(); + + public void open() { + oidAdapterMap.open(); + pojoAdapterMap.open(); + } + + public void close() { + + try { + oidAdapterMap.close(); + } catch(final Throwable ex) { + // ignore + LOG.error("close: oidAdapterMap#close() failed; continuing to avoid memory leakage"); + } + + try { + pojoAdapterMap.close(); + } catch(final Throwable ex) { + // ignore + LOG.error("close: pojoAdapterMap#close() failed; continuing to avoid memory leakage"); + } + } + + private ObjectAdapter lookupAdapterByPojo(Object pojo) { + final ObjectAdapter adapter = pojoAdapterMap.getAdapter(pojo); + return adapter; + } + + private void putPojo(Object pojo, ObjectAdapter adapter) { + pojoAdapterMap.add(adapter.getObject(), adapter); + } + + private void removePojo(ObjectAdapter adapter) { + pojoAdapterMap.remove(adapter); + } + + private ObjectAdapter lookupAdapterById(Oid oid) { + return oidAdapterMap.getAdapter(oid); + } + + private void addAdapter(ObjectAdapter adapter) { + if(adapter==null) { + return; // nothing to do + } + final Oid oid = adapter.getOid(); + if (oid != null) { // eg. value objects don't have an Oid + oidAdapterMap.add(oid, adapter); + } + putPojo(adapter.getObject(), adapter); + } + + private void removeAdapter(ObjectAdapter adapter) { + LOG.debug("removing adapter: {}", adapter); + if(adapter==null) { + return; // nothing to do + } + final Oid oid = adapter.getOid(); + if (oid != null) { // eg. value objects don't have an Oid + oidAdapterMap.remove(oid); + } + removePojo(adapter); + } + + } + + private final Cache cache = new Cache(); + private final PersistenceSession persistenceSession; private final ServicesInjector servicesInjector; private final SpecificationLoader specificationLoader; @@ -102,103 +175,47 @@ public class ObjectAdapterContext { // -- LIFE-CYCLING public void open() { - oidAdapterMap.open(); - pojoAdapterMap.open(); - oidAdapterMap2.open(); + cache.open(); initServices(); } public void close() { - - try { - oidAdapterMap.close(); - oidAdapterMap2.close(); - } catch(final Throwable ex) { - // ignore - LOG.error("close: oidAdapterMap#close() failed; continuing to avoid memory leakage"); - } - - try { - pojoAdapterMap.close(); - } catch(final Throwable ex) { - // ignore - LOG.error("close: pojoAdapterMap#close() failed; continuing to avoid memory leakage"); - } + cache.close(); } - // -- CACHING DEPR. + // -- CACHING POJO @Deprecated // don't expose caching protected ObjectAdapter lookupAdapterByPojo(Object pojo) { - final ObjectAdapter adapter = pojoAdapterMap.getAdapter(pojo); - if(adapter!=null) { - Oid y = adapter.getOid(); - Objects.requireNonNull(y); - - Oid x = objectAdapterProviderMixin.oidFor(pojo); - Objects.requireNonNull(x); - - //FIXME[ISIS-1976] oids must match - //Ensure.ensureThatArg(x, _Predicates.equalTo(y), ()->String.format("x: %s\ny: %s", ""+x, ""+y)); - } - return adapter; - } - - private void putPojo(Object pojo, ObjectAdapter adapter) { - - Oid x = objectAdapterProviderMixin.oidFor(pojo); - Oid y = adapter.getOid(); - Objects.requireNonNull(y); - - //FIXME[ISIS-1976] oids must match - //Ensure.ensureThatArg(x, _Predicates.equalTo(y), ()->String.format("x: %s\ny: %s", ""+x, ""+y)); - - oidAdapterMap2.add(adapter.getOid(), adapter); - pojoAdapterMap.add(adapter.getObject(), adapter); + return cache.lookupAdapterByPojo(pojo); } - private void removePojo(ObjectAdapter adapter) { - Oid y = adapter.getOid(); - Objects.requireNonNull(y); - oidAdapterMap2.remove(y); - pojoAdapterMap.remove(adapter); - } - - // -- CACHING - @Deprecated // don't expose caching public boolean containsAdapterForPojo(Object pojo) { return lookupAdapterByPojo(pojo)!=null; } + // -- CACHING OID + @Deprecated // don't expose caching protected ObjectAdapter lookupAdapterById(Oid oid) { - return oidAdapterMap.getAdapter(oid); + return cache.lookupAdapterById(oid); } + + private OidAdapterHashMap oidAdapterMap() { + return cache.oidAdapterMap; + } + + // -- CACHING BOTH @Deprecated // don't expose caching public void addAdapter(ObjectAdapter adapter) { - if(adapter==null) { - return; // nothing to do - } - final Oid oid = adapter.getOid(); - if (oid != null) { // eg. value objects don't have an Oid - oidAdapterMap.add(oid, adapter); - } - putPojo(adapter.getObject(), adapter); + cache.addAdapter(adapter); } @Deprecated // don't expose caching public void removeAdapter(ObjectAdapter adapter) { - LOG.debug("removing adapter: {}", adapter); - if(adapter==null) { - return; // nothing to do - } - final Oid oid = adapter.getOid(); - if (oid != null) { // eg. value objects don't have an Oid - oidAdapterMap.remove(oid); - } - removePojo(adapter); + cache.removeAdapter(adapter); } // -- CACHE CONSISTENCY @@ -327,7 +344,8 @@ public class ObjectAdapterContext { // remap as Persistent if required if (serviceAdapter.getOid().isTransient()) { - remapAsPersistent(serviceAdapter, null, persistenceSession); + _Exceptions.unexpectedCodeReach(); + //remapAsPersistent(serviceAdapter, null, persistenceSession); } } } @@ -337,11 +355,11 @@ public class ObjectAdapterContext { // add all aggregated collections final ObjectSpecification objSpec = adapter.getSpecification(); if (!adapter.isParentedCollection() || adapter.isParentedCollection() && !objSpec.isImmutable()) { - putPojo(pojo, adapter); + cache.putPojo(pojo, adapter); } // order is important - add to pojo map first, then identity map - oidAdapterMap.add(adapter.getOid(), adapter); + oidAdapterMap().add(adapter.getOid(), adapter); } public ObjectAdapter disposableAdapterForViewModel(Object viewModelPojo) { @@ -367,9 +385,6 @@ public class ObjectAdapterContext { } /** - * was in PersisenceSessionX, temporarily moved here to successfully compile - * - * <p> * Note that there is no management of {@link Version}s here. That is * because the {@link PersistenceSession} is expected to manage this. * @@ -434,7 +449,7 @@ public class ObjectAdapterContext { for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) { final ParentedCollectionOid previousCollectionOid = (ParentedCollectionOid) collectionAdapter.getOid(); final ParentedCollectionOid persistedCollectionOid = previousCollectionOid.asPersistent(persistedRootOid); - oidAdapterMap.add(persistedCollectionOid, collectionAdapter); + oidAdapterMap().add(persistedCollectionOid, collectionAdapter); } // some object store implementations may replace collection instances (eg ORM may replace with a cglib-enhanced @@ -449,9 +464,9 @@ public class ObjectAdapterContext { final Object collectionPojoActuallyOnPojo = getCollectionPojo(otma, adapterReplacement); if (collectionPojoActuallyOnPojo != collectionPojoWrappedByAdapter) { - removePojo(collectionAdapter); + cache.removePojo(collectionAdapter); final ObjectAdapter newCollectionAdapter = collectionAdapter.withPojo(collectionPojoActuallyOnPojo); - putPojo(collectionPojoActuallyOnPojo, newCollectionAdapter); + cache.putPojo(collectionPojoActuallyOnPojo, newCollectionAdapter); } } @@ -460,10 +475,10 @@ public class ObjectAdapterContext { private void removeFromCache( final RootAndCollectionAdapters rootAndCollectionAdapters, final RootOid transientRootOid) { - LOG.debug("remapAsPersistent: {}", transientRootOid); + LOG.debug("removing root adapter from oid map"); - boolean removed = oidAdapterMap.remove(transientRootOid); + boolean removed = oidAdapterMap().remove(transientRootOid); if (!removed) { LOG.warn("could not remove oid: {}", transientRootOid); // should we fail here with a more serious error? @@ -474,7 +489,7 @@ public class ObjectAdapterContext { } for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) { final Oid collectionOid = collectionAdapter.getOid(); - removed = oidAdapterMap.remove(collectionOid); + removed = oidAdapterMap().remove(collectionOid); if (!removed) { ObjectAdapterLegacy.LOG.warn("could not remove collectionOid: {}", collectionOid); // should we fail here with a more serious error? @@ -493,11 +508,8 @@ public class ObjectAdapterContext { @Deprecated public ObjectAdapter remapRecreatedPojo(ObjectAdapter adapter, final Object pojo) { final ObjectAdapter newAdapter = adapter.withPojo(pojo); - removePojo(adapter); - removePojo(newAdapter); - - oidAdapterMap.remove(adapter.getOid()); - oidAdapterMap.remove(newAdapter.getOid()); + cache.removeAdapter(adapter); + cache.removeAdapter(newAdapter); //FIXME[ISIS-1976] can't remove yet, does have strange side-effects if(true){ diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_AdapterManager.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_AdapterManager.java index e60d247..7936a9d 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_AdapterManager.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_AdapterManager.java @@ -23,7 +23,9 @@ import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.isis.commons.internal.functions._Predicates; import org.apache.isis.core.commons.ensure.Assert; +import org.apache.isis.core.commons.ensure.Ensure; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.oid.Oid; import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid; @@ -149,7 +151,6 @@ class ObjectAdapterContext_AdapterManager { // since the whole point of this method is to map an adapter that's just been created. // so we *don't* call ensureMapsConsistent(adapter); - Assert.assertNotNull(adapter); final Object pojo = adapter.getObject(); Assert.assertFalse("POJO Map already contains object", pojo, objectAdapterContext.containsAdapterForPojo(pojo)); diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterProvider.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterProvider.java index 77545ab..593ad6c 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterProvider.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterProvider.java @@ -72,8 +72,9 @@ class ObjectAdapterContext_ObjectAdapterProvider implements ObjectAdapterProvide if(persistentOrValueOid != null) { return persistentOrValueOid; } - // Creates a new transient root for the supplied domain object - final RootOid rootOid = persistenceSession.createTransientOrViewModelOid(pojo); + final RootOid rootOid = servicesInjector.isRegisteredServiceInstance(pojo) + ? persistenceSession.createPersistentOrViewModelOid(pojo) + : persistenceSession.createTransientOrViewModelOid(pojo); return rootOid; } @@ -88,8 +89,11 @@ class ObjectAdapterContext_ObjectAdapterProvider implements ObjectAdapterProvide return existingOrValueAdapter; } - // Creates a new transient root {@link ObjectAdapter adapter} for the supplied domain - final RootOid rootOid = persistenceSession.createTransientOrViewModelOid(pojo); + final RootOid rootOid = servicesInjector.isRegisteredServiceInstance(pojo) + ? persistenceSession.createPersistentOrViewModelOid(pojo) + : persistenceSession.createTransientOrViewModelOid(pojo); + +// final RootOid rootOid = persistenceSession.createTransientOrViewModelOid(pojo); final ObjectAdapter newAdapter = objectAdapterContext.getFactories().createRootAdapter(pojo, rootOid); return objectAdapterContext.mapAndInjectServices(newAdapter);