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 2a7dd9dad4d573dd874b09188f60be9191834f8a Author: Andi Huber <ahu...@apache.org> AuthorDate: Mon Sep 10 10:57:54 2018 +0200 ISIS-1976: refactor OA-by-Oid supply responsibility into new interface introduces ObjectAdapterByIdProvider Task-Url: https://issues.apache.org/jira/browse/ISIS-1976 --- .../adapter/ObjectAdapterByIdProvider.java | 86 +++++++++ .../system/persistence/PersistenceSession4.java | 79 +++----- .../persistence/PersistenceSession4_Decouple.java | 213 --------------------- .../system/persistence/PersistenceSession5.java | 67 ++----- .../system/persistence/PersistenceSession.java | 36 ++-- .../adaptermanager/ObjectAdapterContext.java | 12 ++ ...tAdapterContext_ObjectAdapterByIdProvider.java} | 129 +++++++++---- .../wicket/model/models/EntityCollectionModel.java | 12 +- 8 files changed, 254 insertions(+), 380 deletions(-) diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/ObjectAdapterByIdProvider.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/ObjectAdapterByIdProvider.java new file mode 100644 index 0000000..d8928bf --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/adapter/ObjectAdapterByIdProvider.java @@ -0,0 +1,86 @@ +/* + * 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.adapter; + +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.isis.applib.annotation.Programmatic; +import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; +import org.apache.isis.core.metamodel.adapter.oid.RootOid; + +/** + * + * @since 2.0.0-M2 + * + */ +public interface ObjectAdapterByIdProvider { + + // -- INTERFACE + + ObjectAdapter adapterFor(RootOid rootOid, ConcurrencyChecking concurrencyChecking); + Map<RootOid, ObjectAdapter> adaptersFor(Stream<RootOid> rootOids, ConcurrencyChecking concurrencyChecking); + + /** + * As per {@link #adapterFor(RootOid, ConcurrencyChecking)}, with + * {@link ConcurrencyChecking#NO_CHECK no checking}. + * + * <p> + * This method will <i>always</i> return an object, possibly indicating it is persistent; so make sure that you + * know that the oid does indeed represent an object you know exists. + * </p> + */ + default ObjectAdapter adapterFor(final RootOid rootOid) { + return adapterFor(rootOid, ConcurrencyChecking.NO_CHECK); + } + + default Map<RootOid, ObjectAdapter> adaptersFor(Stream<RootOid> rootOids) { + return adaptersFor(rootOids, ConcurrencyChecking.NO_CHECK); + } + + + // -- FOR THOSE THAT IMPLEMENT THROUGH DELEGATION + + public static interface Delegating extends ObjectAdapterByIdProvider { + + @Programmatic + ObjectAdapterByIdProvider getObjectAdapterByIdProvider(); + + @Programmatic + default ObjectAdapter adapterFor(RootOid rootOid, ConcurrencyChecking concurrencyChecking) { + return getObjectAdapterByIdProvider().adapterFor(rootOid, concurrencyChecking); + } + + + @Programmatic + default Map<RootOid, ObjectAdapter> adaptersFor(Stream<RootOid> rootOids, ConcurrencyChecking concurrencyChecking) { + return getObjectAdapterByIdProvider().adaptersFor(rootOids, concurrencyChecking); + } + + + } + + + + + + + + +} diff --git a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java b/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java index 7ba1caa..390853a 100644 --- a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java +++ b/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java @@ -18,12 +18,14 @@ */ package org.apache.isis.core.runtime.system.persistence; +import static java.util.Objects.requireNonNull; import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -38,7 +40,6 @@ import javax.jdo.PersistenceManagerFactory; import javax.jdo.identity.SingleFieldIdentity; import javax.jdo.listener.InstanceLifecycleListener; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.datanucleus.enhancement.Persistable; @@ -54,12 +55,12 @@ import org.apache.isis.applib.services.bookmark.BookmarkService; import org.apache.isis.applib.services.command.Command; import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer; import org.apache.isis.applib.services.iactn.Interaction; -import org.apache.isis.commons.internal.collections._Lists; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.ensure.Assert; import org.apache.isis.core.commons.exceptions.IsisException; import org.apache.isis.core.commons.factory.InstanceUtil; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.oid.Oid; @@ -127,7 +128,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { private static final Logger LOG = LoggerFactory.getLogger(PersistenceSession4.class); private ObjectAdapterContext objectAdapterContext; - private PersistenceSession4_Decouple mixin; /** * Initialize the object store so that calls to this object store access @@ -170,7 +170,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { new PersistenceQueryFindUsingApplibQueryProcessor(this)); objectAdapterContext = ObjectAdapterContext.openContext(servicesInjector, authenticationSession, specificationLoader, this); - mixin = new PersistenceSession4_Decouple(this, objectAdapterContext); // tell the proxy of all request-scoped services to instantiate the underlying // services, store onto the thread-local and inject into them... @@ -477,6 +476,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { return initializePropertiesAndDoCallback(adapter); } + //FIXME[ISIS-1976] remove private Object recreateViewModel(final ObjectSpecification spec, final String memento) { final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class); if(facet == null) { @@ -695,19 +695,16 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { public ObjectAdapter execute() { LOG.debug("getObject; oid={}", oid); - final Object pojo = loadPersistentPojo(oid); + final Object pojo = fetchPersistentPojo(oid); return objectAdapterContext.addRecreatedPojoToCache(oid, pojo); } }); } + // -- FETCHING - - - // -- loadPersistentPojo - - //TODO[ISIS-1976] used by mixin - Object loadPersistentPojo(final RootOid rootOid) { + @Override + public Object fetchPersistentPojo(final RootOid rootOid) { Object result; try { @@ -739,14 +736,14 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { return result; } - //TODO[ISIS-1976] used by mixin - Map<RootOid,Object> loadPersistentPojos(final List<RootOid> rootOids) { + @Override + public Map<RootOid,Object> fetchPersistentPojos(final List<RootOid> rootOids) { if(rootOids.isEmpty()) { - return zip(rootOids, Collections.emptyList()); + return Collections.emptyMap(); } - final List<Object> dnOids = _Lists.newArrayList(); + final List<Object> dnOids = new ArrayList<>(rootOids.size()); for (final RootOid rootOid : rootOids) { final Object id = JdoObjectIdSerializer.toJdoObjectId(rootOid); if(id instanceof SingleFieldIdentity) { @@ -762,7 +759,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { } FetchPlan fetchPlan = persistenceManager.getFetchPlan(); fetchPlan.addGroup(FetchGroup.DEFAULT); - final List<Object> persistentPojos = Lists.newArrayList(); + final List<Object> persistentPojos = new ArrayList<>(rootOids.size()); try { final Collection<Object> pojos = uncheckedCast(persistenceManager.getObjectsById(dnOids, true)); for (final Object pojo : pojos) { @@ -1077,40 +1074,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { } } - @Override - public Map<RootOid, ObjectAdapter> adaptersFor( - final List<RootOid> rootOids, - final ConcurrencyChecking concurrencyChecking) { - - return mixin.adaptersFor(rootOids, concurrencyChecking); - } - - @Override - public ObjectAdapter adapterFor( - final RootOid rootOid, - final ConcurrencyChecking concurrencyChecking) { - - return mixin.adapterFor(rootOid, concurrencyChecking); - } - - //TODO[ISIS-1976] used by mixin - Object recreatePojoTransientOrViewModel(final RootOid rootOid) { - final ObjectSpecification spec = - specificationLoader.lookupBySpecId(rootOid.getObjectSpecId()); - final Object pojo; - - if(rootOid.isViewModel()) { - - final String memento = rootOid.getIdentifier(); - pojo = recreateViewModel(spec, memento); - - } else { - pojo = instantiateAndInjectServices(spec); - - } - return pojo; - } - // -- TransactionManager delegate methods protected IsisTransaction getCurrentTransaction() { return transactionManager.getCurrentTransaction(); @@ -1202,7 +1165,15 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { @Override public String identifierFor(final Object pojo) { - return JdoObjectIdSerializer.toOidIdentifier(getPersistenceManager().getObjectId(pojo)); + final Object jdoOid = getPersistenceManager().getObjectId(pojo); + if(jdoOid==null) { + return UUID.randomUUID().toString(); //FIXME[ISIS-1976] should be guarded against somewhere else + } + + requireNonNull(jdoOid, + ()->String.format("Pojo of type '%s' is not recognized by JDO.", + pojo.getClass().getName())); + return JdoObjectIdSerializer.toOidIdentifier(jdoOid); } @@ -1408,6 +1379,12 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { return objectAdapterContext.getObjectAdapterProvider(); } + @Override + public ObjectAdapterByIdProvider getObjectAdapterByIdProvider() { + return objectAdapterContext.getObjectAdapterByIdProvider(); + } + + } diff --git a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4_Decouple.java b/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4_Decouple.java deleted file mode 100644 index de33ef0..0000000 --- a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4_Decouple.java +++ /dev/null @@ -1,213 +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.runtime.system.persistence; - -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.isis.commons.internal.collections._Lists; -import org.apache.isis.commons.internal.collections._Maps; -import org.apache.isis.core.commons.authentication.AuthenticationSession; -import org.apache.isis.core.metamodel.adapter.ObjectAdapter; -import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; -import org.apache.isis.core.metamodel.adapter.oid.Oid; -import org.apache.isis.core.metamodel.adapter.oid.RootOid; -import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; -import org.apache.isis.core.metamodel.adapter.version.Version; -import org.apache.isis.core.runtime.persistence.ObjectNotFoundException; -import org.apache.isis.core.runtime.persistence.PojoRecreationException; -import org.apache.isis.core.runtime.system.persistence.adaptermanager.ObjectAdapterContext; - -class PersistenceSession4_Decouple { - - private static final Logger LOG = LoggerFactory.getLogger(PersistenceSession4_Decouple.class); - private final PersistenceSession4 holder; - private final ObjectAdapterContext objectAdapterContext; - private final AuthenticationSession authenticationSession; - private final boolean concurrencyCheckingGloballyEnabled; - - protected PersistenceSession4_Decouple(PersistenceSession4 holder, ObjectAdapterContext objectAdapterContext) { - this.holder = holder; - this.objectAdapterContext = objectAdapterContext; - this.authenticationSession = holder.getAuthenticationSession(); - this.concurrencyCheckingGloballyEnabled = !ConcurrencyChecking.isGloballyDisabled(holder.getConfiguration()); - } - - /** - * Either returns an existing {@link ObjectAdapter adapter} (as per - * {@link #lookupAdapterFor(Oid)}), otherwise re-creates an adapter with the - * specified (persistent) {@link Oid}. - * - * <p> - * Typically called when the {@link Oid} is already known, that is, when - * resolving an already-persisted object. Is also available for - * <tt>Memento</tt> support however, so {@link Oid} could also represent a - * {@link Oid#isTransient() transient} object. - * - * <p> - * The pojo itself is recreated by delegating to a {@link AdapterManager}. - * - * <p> - * The {@link ConcurrencyChecking} parameter determines whether concurrency checking is performed. - * If it is requested, then a check is made to ensure that the {@link Oid#getVersion() version} - * of the {@link RootOid oid} of the recreated adapter is the same as that of the provided {@link RootOid oid}. - * If the version differs, then a {@link ConcurrencyException} is thrown. - * - * <p> - * ALSO, even if a {@link ConcurrencyException}, then the provided {@link RootOid oid}'s {@link Version version} - * will be {@link RootOid#setVersion(Version) set} to the current - * value. This allows the client to retry if they wish. - * - * @throws {@link org.apache.isis.core.runtime.persistence.ObjectNotFoundException} if the object does not exist. - */ - public ObjectAdapter adapterFor( - final RootOid rootOid, - final ConcurrencyChecking concurrencyChecking) { - - // attempt to locate adapter for the Oid - ObjectAdapter adapter = objectAdapterContext.lookupAdapterFor(rootOid); - if (adapter == null) { - // else recreate - try { - final Object pojo; - if(rootOid.isTransient() || rootOid.isViewModel()) { - pojo = holder.recreatePojoTransientOrViewModel(rootOid); - } else { - pojo = holder.loadPersistentPojo(rootOid); - } - adapter = objectAdapterContext.addRecreatedPojoToCache(rootOid, pojo); - } catch(ObjectNotFoundException ex) { - throw ex; // just rethrow - } catch(RuntimeException ex) { - throw new PojoRecreationException(rootOid, ex); - } - } - - // sync versions of original, with concurrency checking if required - syncVersion(concurrencyChecking, adapter, rootOid); - - return adapter; - - } - - protected Map<RootOid,ObjectAdapter> adaptersFor( - final List<RootOid> rootOids, - final ConcurrencyChecking concurrencyChecking) { - - final Map<RootOid, ObjectAdapter> adapterByOid = _Maps.newLinkedHashMap(); - - List<RootOid> notYetLoadedOids = _Lists.newArrayList(); - for (RootOid rootOid : rootOids) { - // attempt to locate adapter for the Oid - ObjectAdapter adapter = objectAdapterContext.lookupAdapterFor(rootOid); - // handle view models or transient - if (adapter == null) { - if (rootOid.isTransient() || rootOid.isViewModel()) { - final Object pojo = holder.recreatePojoTransientOrViewModel(rootOid); - adapter = objectAdapterContext.addRecreatedPojoToCache(rootOid, pojo); - syncVersion(concurrencyChecking, adapter, rootOid); - } - } - if (adapter != null) { - adapterByOid.put(rootOid, adapter); - } else { - // persistent oid, to load in bulk - notYetLoadedOids.add(rootOid); - } - } - - // recreate, in bulk, all those not yet loaded - final Map<RootOid, Object> pojoByOid = holder.loadPersistentPojos(notYetLoadedOids); - for (Map.Entry<RootOid, Object> entry : pojoByOid.entrySet()) { - final RootOid rootOid = entry.getKey(); - final Object pojo = entry.getValue(); - if(pojo != null) { - ObjectAdapter adapter; - try { - adapter = objectAdapterContext.addRecreatedPojoToCache(rootOid, pojo); - adapterByOid.put(rootOid, adapter); - } catch(ObjectNotFoundException ex) { - throw ex; // just rethrow - } catch(RuntimeException ex) { - throw new PojoRecreationException(rootOid, ex); - } - syncVersion(concurrencyChecking, adapter, rootOid); - } else { - // null indicates it couldn't be loaded - // do nothing here... - } - } - - return adapterByOid; - } - - private void syncVersion( - final ConcurrencyChecking concurrencyChecking, - final ObjectAdapter adapter, final RootOid rootOid) { - // sync versions of original, with concurrency checking if required - Oid adapterOid = adapter.getOid(); - if(adapterOid instanceof RootOid) { - final RootOid recreatedOid = (RootOid) adapterOid; - final RootOid originalOid = rootOid; - - try { - if(concurrencyChecking.isChecking()) { - - // check for exception, but don't throw if suppressed through thread-local - final Version otherVersion = originalOid.getVersion(); - final Version thisVersion = recreatedOid.getVersion(); - if( thisVersion != null && - otherVersion != null && - thisVersion.different(otherVersion)) { - - if(concurrencyCheckingGloballyEnabled && ConcurrencyChecking.isCurrentlyEnabled()) { - LOG.info("concurrency conflict detected on {} ({})", recreatedOid, otherVersion); - final String currentUser = authenticationSession.getUserName(); - throw new ConcurrencyException(currentUser, recreatedOid, thisVersion, otherVersion); - } else { - LOG.info("concurrency conflict detected but suppressed, on {} ({})", recreatedOid, otherVersion); - } - } - } - } finally { - final Version originalVersion = originalOid.getVersion(); - final Version recreatedVersion = recreatedOid.getVersion(); - if(recreatedVersion != null && ( - originalVersion == null || - recreatedVersion.different(originalVersion)) - ) { - if(LOG.isDebugEnabled()) { - LOG.debug("updating version in oid, on {} ({}) to ({})", originalOid, originalVersion, recreatedVersion); - } - originalOid.setVersion(recreatedVersion); - } - } - } - } - - - - -} - - - diff --git a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java index 2a9ec77..0fae449 100644 --- a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java +++ b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java @@ -25,6 +25,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -34,18 +35,14 @@ import java.util.UUID; import javax.jdo.FetchGroup; import javax.jdo.FetchPlan; -import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import javax.jdo.identity.SingleFieldIdentity; import javax.jdo.listener.InstanceLifecycleListener; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.datanucleus.enhancement.Persistable; -import org.datanucleus.enhancer.methods.IsDeleted; -import org.datanucleus.enhancer.methods.IsPersistent; import org.datanucleus.exceptions.NucleusObjectNotFoundException; import org.datanucleus.identity.DatastoreIdImpl; import org.slf4j.Logger; @@ -58,12 +55,12 @@ import org.apache.isis.applib.services.bookmark.BookmarkService; import org.apache.isis.applib.services.command.Command; import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer; import org.apache.isis.applib.services.iactn.Interaction; -import org.apache.isis.commons.internal.collections._Lists; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.ensure.Assert; import org.apache.isis.core.commons.exceptions.IsisException; import org.apache.isis.core.commons.factory.InstanceUtil; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.oid.Oid; @@ -131,7 +128,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { private static final Logger LOG = LoggerFactory.getLogger(PersistenceSession5.class); private ObjectAdapterContext objectAdapterContext; - private PersistenceSession5_Decouple mixin; /** * Initialize the object store so that calls to this object store access @@ -174,7 +170,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { new PersistenceQueryFindUsingApplibQueryProcessor(this)); objectAdapterContext = ObjectAdapterContext.openContext(servicesInjector, authenticationSession, specificationLoader, this); - mixin = new PersistenceSession5_Decouple(this, objectAdapterContext); // tell the proxy of all request-scoped services to instantiate the underlying // services, store onto the thread-local and inject into them... @@ -479,6 +474,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { return initializePropertiesAndDoCallback(adapter); } + //FIXME[ISIS-1976] remove private Object recreateViewModel(final ObjectSpecification spec, final String memento) { final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class); if(facet == null) { @@ -697,16 +693,16 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { public ObjectAdapter execute() { LOG.debug("getObject; oid={}", oid); - final Object pojo = loadPersistentPojo(oid); + final Object pojo = fetchPersistentPojo(oid); return objectAdapterContext.addRecreatedPojoToCache(oid, pojo); } }); } - // -- loadPersistentPojo + // -- FETCHING - //TODO[ISIS-1976] used by mixin - Object loadPersistentPojo(final RootOid rootOid) { + @Override + public Object fetchPersistentPojo(final RootOid rootOid) { Object result; try { @@ -738,14 +734,14 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { return result; } - //TODO[ISIS-1976] used by mixin - Map<RootOid,Object> loadPersistentPojos(final List<RootOid> rootOids) { + @Override + public Map<RootOid,Object> fetchPersistentPojos(final List<RootOid> rootOids) { if(rootOids.isEmpty()) { - return zip(rootOids, Collections.emptyList()); + return Collections.emptyMap(); } - final List<Object> dnOids = _Lists.newArrayList(); + final List<Object> dnOids = new ArrayList<>(rootOids.size()); for (final RootOid rootOid : rootOids) { final Object id = JdoObjectIdSerializer.toJdoObjectId(rootOid); if(id instanceof SingleFieldIdentity) { @@ -761,7 +757,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { } FetchPlan fetchPlan = persistenceManager.getFetchPlan(); fetchPlan.addGroup(FetchGroup.DEFAULT); - final List<Object> persistentPojos = Lists.newArrayList(); + final List<Object> persistentPojos = new ArrayList<>(rootOids.size()); try { final Collection<Object> pojos = uncheckedCast(persistenceManager.getObjectsById(dnOids, true)); for (final Object pojo : pojos) { @@ -1074,40 +1070,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { } } - @Override - public Map<RootOid, ObjectAdapter> adaptersFor( - final List<RootOid> rootOids, - final ConcurrencyChecking concurrencyChecking) { - - return mixin.adaptersFor(rootOids, concurrencyChecking); - } - - @Override - public ObjectAdapter adapterFor( - final RootOid rootOid, - final ConcurrencyChecking concurrencyChecking) { - - return mixin.adapterFor(rootOid, concurrencyChecking); - } - - //TODO[ISIS-1976] used by mixin - Object recreatePojoTransientOrViewModel(final RootOid rootOid) { - final ObjectSpecification spec = - specificationLoader.lookupBySpecId(rootOid.getObjectSpecId()); - final Object pojo; - - if(rootOid.isViewModel()) { - - final String memento = rootOid.getIdentifier(); - pojo = recreateViewModel(spec, memento); - - } else { - pojo = instantiateAndInjectServices(spec); - - } - return pojo; - } - // -- TransactionManager delegate methods protected IsisTransaction getCurrentTransaction() { return transactionManager.getCurrentTransaction(); @@ -1409,6 +1371,11 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { public ObjectAdapterProvider getObjectAdapterProvider() { return objectAdapterContext.getObjectAdapterProvider(); } + + @Override + public ObjectAdapterByIdProvider getObjectAdapterByIdProvider() { + return objectAdapterContext.getObjectAdapterByIdProvider(); + } } diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java index eec2996..2354136 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java @@ -27,8 +27,8 @@ import org.apache.isis.applib.services.bookmark.BookmarkService.FieldResetPolicy import org.apache.isis.core.commons.components.SessionScopedComponent; import org.apache.isis.core.commons.config.IsisConfiguration; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; -import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid; import org.apache.isis.core.metamodel.adapter.oid.RootOid; import org.apache.isis.core.metamodel.services.ServicesInjector; @@ -39,7 +39,12 @@ import org.apache.isis.core.runtime.runner.opts.OptionHandlerFixtureAbstract; import org.apache.isis.core.runtime.system.persistence.adaptermanager.ObjectAdapterContext.MementoRecreateObjectSupport; import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager; -public interface PersistenceSession extends ObjectAdapterProvider.Delegating, TransactionalResource, SessionScopedComponent { +public interface PersistenceSession +extends + ObjectAdapterProvider.Delegating, + ObjectAdapterByIdProvider.Delegating, + TransactionalResource, + SessionScopedComponent { // -- CONSTANTS @@ -55,27 +60,8 @@ public interface PersistenceSession extends ObjectAdapterProvider.Delegating, Tr MementoRecreateObjectSupport mementoSupport(); - ObjectAdapter adapterFor(RootOid rootOid, ConcurrencyChecking concurrencyChecking); - Map<RootOid, ObjectAdapter> adaptersFor(List<RootOid> rootOids, ConcurrencyChecking concurrencyChecking); ObjectAdapter adapterForAny(RootOid rootOid); <T> List<ObjectAdapter> allMatchingQuery(final Query<T> query); - - /** - * As per {@link #adapterFor(RootOid, ConcurrencyChecking)}, with - * {@link ConcurrencyChecking#NO_CHECK no checking}. - * - * <p> - * This method will <i>always</i> return an object, possibly indicating it is persistent; so make sure that you - * know that the oid does indeed represent an object you know exists. - * </p> - */ - default ObjectAdapter adapterFor(final RootOid rootOid) { - return adapterFor(rootOid, ConcurrencyChecking.NO_CHECK); - } - - default Map<RootOid, ObjectAdapter> adaptersFor(List<RootOid> rootOids) { - return adaptersFor(rootOids, ConcurrencyChecking.NO_CHECK); - } // -- @@ -111,6 +97,10 @@ public interface PersistenceSession extends ObjectAdapterProvider.Delegating, Tr boolean isRepresentingPersistent(Object pojo); /**@since 2.0.0-M2*/ boolean isDestroyed(Object pojo); + /**@since 2.0.0-M2*/ + Object fetchPersistentPojo(RootOid rootOid); + /**@since 2.0.0-M2*/ + Map<RootOid, Object> fetchPersistentPojos(List<RootOid> rootOids); /** * Convenient equivalent to {@code getPersistenceManager()}. @@ -167,8 +157,6 @@ public interface PersistenceSession extends ObjectAdapterProvider.Delegating, Tr void resolve(Object parent); - - - + } 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 6f5df49..f4a21fa 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 @@ -29,7 +29,9 @@ import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.ensure.Assert; import org.apache.isis.core.commons.ensure.IsisAssertException; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; +import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.oid.Oid; import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid; import org.apache.isis.core.metamodel.adapter.oid.RootOid; @@ -131,6 +133,7 @@ public class ObjectAdapterContext { private final ObjectAdapterContext_MementoSupport mementoSupportMixin; private final ObjectAdapterContext_ServiceLookup serviceLookupMixin; private final ObjectAdapterContext_NewIdentifier newIdentifierMixin; + private final ObjectAdapterContext_ObjectAdapterByIdProvider byIdMixin; private ObjectAdapterContext( ServicesInjector servicesInjector, @@ -144,6 +147,7 @@ public class ObjectAdapterContext { this.mementoSupportMixin = new ObjectAdapterContext_MementoSupport(this, persistenceSession); this.serviceLookupMixin = new ObjectAdapterContext_ServiceLookup(this, servicesInjector); this.newIdentifierMixin = new ObjectAdapterContext_NewIdentifier(this, persistenceSession); + this.byIdMixin = new ObjectAdapterContext_ObjectAdapterByIdProvider(this, persistenceSession, authenticationSession); this.persistenceSession = persistenceSession; this.servicesInjector = servicesInjector; @@ -233,6 +237,12 @@ public class ObjectAdapterContext { return serviceLookupMixin.lookupServiceAdapterFor(rootOid); } + // -- BY-ID SUPPORT + + public ObjectAdapterByIdProvider getObjectAdapterByIdProvider() { + return byIdMixin; + } + // -- FACTORIES // package private @@ -465,6 +475,8 @@ public class ObjectAdapterContext { return newAdapter; } + + diff --git a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5_Decouple.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterByIdProvider.java similarity index 71% rename from core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5_Decouple.java rename to core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterByIdProvider.java index e40f9c3..c4a8af3 100644 --- a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5_Decouple.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectAdapterByIdProvider.java @@ -16,43 +16,64 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.isis.core.runtime.system.persistence; +package org.apache.isis.core.runtime.system.persistence.adaptermanager; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.isis.commons.internal.collections._Lists; import org.apache.isis.commons.internal.collections._Maps; -import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.concurrency.ConcurrencyChecking; import org.apache.isis.core.metamodel.adapter.oid.Oid; import org.apache.isis.core.metamodel.adapter.oid.RootOid; import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.core.metamodel.adapter.version.Version; +import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; +import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.core.runtime.persistence.ObjectNotFoundException; import org.apache.isis.core.runtime.persistence.PojoRecreationException; -import org.apache.isis.core.runtime.system.persistence.adaptermanager.ObjectAdapterContext; +import org.apache.isis.core.runtime.system.persistence.PersistenceSession; -class PersistenceSession5_Decouple { - - private static final Logger LOG = LoggerFactory.getLogger(PersistenceSession5_Decouple.class); - private final PersistenceSession5 holder; +/** + * package private mixin for ObjectAdapterContext + * <p> + * Responsibility: creates RootOids + * </p> + * @since 2.0.0-M2 + */ +class ObjectAdapterContext_ObjectAdapterByIdProvider implements ObjectAdapterByIdProvider { + + + private static final Logger LOG = LoggerFactory.getLogger(ObjectAdapterContext_ObjectAdapterByIdProvider.class); private final ObjectAdapterContext objectAdapterContext; + private final PersistenceSession persistenceSession; + private final ServicesInjector servicesInjector; + private final SpecificationLoader specificationLoader; private final AuthenticationSession authenticationSession; private final boolean concurrencyCheckingGloballyEnabled; - - protected PersistenceSession5_Decouple(PersistenceSession5 holder, ObjectAdapterContext objectAdapterContext) { - this.holder = holder; + + + ObjectAdapterContext_ObjectAdapterByIdProvider(ObjectAdapterContext objectAdapterContext, + PersistenceSession persistenceSession, AuthenticationSession authenticationSession) { this.objectAdapterContext = objectAdapterContext; - this.authenticationSession = holder.getAuthenticationSession(); - this.concurrencyCheckingGloballyEnabled = !ConcurrencyChecking.isGloballyDisabled(holder.getConfiguration()); + this.persistenceSession = persistenceSession; + this.servicesInjector = persistenceSession.getServicesInjector(); + this.specificationLoader = servicesInjector.getSpecificationLoader(); + this.authenticationSession = authenticationSession; + + this.concurrencyCheckingGloballyEnabled = + !ConcurrencyChecking.isGloballyDisabled(persistenceSession.getConfiguration()); } - + /** * Either returns an existing {@link ObjectAdapter adapter} (as per * {@link #lookupAdapterFor(Oid)}), otherwise re-creates an adapter with the @@ -65,7 +86,7 @@ class PersistenceSession5_Decouple { * {@link Oid#isTransient() transient} object. * * <p> - * The pojo itself is recreated by delegating to a {@link AdapterManager}. + * The pojo itself is recreated by delegating to a FIXME:AdapterManager * * <p> * The {@link ConcurrencyChecking} parameter determines whether concurrency checking is performed. @@ -80,11 +101,12 @@ class PersistenceSession5_Decouple { * * @throws {@link org.apache.isis.core.runtime.persistence.ObjectNotFoundException} if the object does not exist. */ + @Override public ObjectAdapter adapterFor( final RootOid rootOid, final ConcurrencyChecking concurrencyChecking) { - - /* FIXME[ISIS-1976] guard against service lookup + + /* FIXME[ISIS-1976] * https://github.com/apache/isis/pull/121#discussion_r215889748 * * Eventually I'm hoping that this code will simplify and then become pluggable. @@ -101,10 +123,11 @@ class PersistenceSession5_Decouple { * into some other datastore. So really my "PersistenceProvider" is a * generalization of that concept). */ - + + //FIXME[ISIS-1976] remove guard final ObjectAdapter serviceAdapter = objectAdapterContext.lookupServiceAdapterFor(rootOid); if (serviceAdapter != null) { - _Exceptions.unexpectedCodeReach(); + //_Exceptions.unexpectedCodeReach(); return serviceAdapter; } @@ -115,9 +138,9 @@ class PersistenceSession5_Decouple { try { final Object pojo; if(rootOid.isTransient() || rootOid.isViewModel()) { - pojo = holder.recreatePojoTransientOrViewModel(rootOid); + pojo = recreatePojoTransientOrViewModel(rootOid); } else { - pojo = holder.loadPersistentPojo(rootOid); + pojo = persistenceSession.fetchPersistentPojo(rootOid); } adapter = objectAdapterContext.addRecreatedPojoToCache(rootOid, pojo); } catch(ObjectNotFoundException ex) { @@ -131,23 +154,25 @@ class PersistenceSession5_Decouple { syncVersion(concurrencyChecking, adapter, rootOid); return adapter; - + } - protected Map<RootOid,ObjectAdapter> adaptersFor( - final List<RootOid> rootOids, + @Override + public Map<RootOid,ObjectAdapter> adaptersFor( + final Stream<RootOid> rootOids, final ConcurrencyChecking concurrencyChecking) { final Map<RootOid, ObjectAdapter> adapterByOid = _Maps.newLinkedHashMap(); List<RootOid> notYetLoadedOids = _Lists.newArrayList(); - for (RootOid rootOid : rootOids) { - // attempt to locate adapter for the Oid + + rootOids.forEach(rootOid->{ + // attempt to locate adapter for the Oid ObjectAdapter adapter = objectAdapterContext.lookupAdapterFor(rootOid); // handle view models or transient if (adapter == null) { if (rootOid.isTransient() || rootOid.isViewModel()) { - final Object pojo = holder.recreatePojoTransientOrViewModel(rootOid); + final Object pojo = recreatePojoTransientOrViewModel(rootOid); adapter = objectAdapterContext.addRecreatedPojoToCache(rootOid, pojo); syncVersion(concurrencyChecking, adapter, rootOid); } @@ -158,10 +183,10 @@ class PersistenceSession5_Decouple { // persistent oid, to load in bulk notYetLoadedOids.add(rootOid); } - } - + }); + // recreate, in bulk, all those not yet loaded - final Map<RootOid, Object> pojoByOid = holder.loadPersistentPojos(notYetLoadedOids); + final Map<RootOid, Object> pojoByOid = persistenceSession.fetchPersistentPojos(notYetLoadedOids); for (Map.Entry<RootOid, Object> entry : pojoByOid.entrySet()) { final RootOid rootOid = entry.getKey(); final Object pojo = entry.getValue(); @@ -184,7 +209,42 @@ class PersistenceSession5_Decouple { return adapterByOid; } + + // -- HELPER + + private Object recreatePojoTransientOrViewModel(final RootOid rootOid) { + final ObjectSpecification spec = + specificationLoader.lookupBySpecId(rootOid.getObjectSpecId()); + final Object pojo; + if(rootOid.isViewModel()) { + + final String memento = rootOid.getIdentifier(); + pojo = recreateViewModel(spec, memento); + + } else { + pojo = persistenceSession.instantiateAndInjectServices(spec); + + } + return pojo; + } + + private Object recreateViewModel(final ObjectSpecification spec, final String memento) { + final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class); + if(facet == null) { + throw new IllegalArgumentException("spec does not have ViewModelFacet; spec is " + spec.getFullIdentifier()); + } + + final Object viewModelPojo; + if(facet.getRecreationMechanism().isInitializes()) { + viewModelPojo = persistenceSession.instantiateAndInjectServices(spec); + facet.initialize(viewModelPojo, memento); + } else { + viewModelPojo = facet.instantiate(spec.getCorrespondingClass(), memento); + } + return viewModelPojo; + } + private void syncVersion( final ConcurrencyChecking concurrencyChecking, final ObjectAdapter adapter, final RootOid rootOid) { @@ -228,11 +288,6 @@ class PersistenceSession5_Decouple { } } } - - - - -} - - - + + +} \ No newline at end of file diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java index 5970611..9f86432 100644 --- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java +++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java @@ -19,6 +19,8 @@ package org.apache.isis.viewer.wicket.model.models; +import static org.apache.isis.commons.internal.base._NullSafe.stream; + import java.io.Serializable; import java.util.Collection; import java.util.Collections; @@ -97,18 +99,18 @@ UiHintContainer { final PersistenceSession persistenceSession = model.getPersistenceSession(); - final List<RootOid> rootOids = - _Lists.transform(model.mementoList, ObjectAdapterMemento.Functions.toOid()); - + final Stream<RootOid> rootOids = stream(model.mementoList) + .map(ObjectAdapterMemento.Functions.toOid()); + final Map<RootOid, ObjectAdapter> adaptersByOid = persistenceSession.adaptersFor(rootOids); final Collection<ObjectAdapter> adapterList = adaptersByOid.values(); - return _NullSafe.stream(adapterList) + return stream(adapterList) .filter(_NullSafe::isPresent) .collect(Collectors.toList()); } private Iterable<ObjectAdapter> loadOneByOne(final EntityCollectionModel model) { - return _NullSafe.stream(model.mementoList) + return stream(model.mementoList) .map(ObjectAdapterMemento.Functions.fromMemento( ConcurrencyChecking.NO_CHECK, model.getPersistenceSession(),