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 2015/09/15 08:03:40 UTC
[35/50] [abbrv] isis git commit: ISIS-1194: inlined AdapterManager
functionality back into PersistenceSession
ISIS-1194: inlined AdapterManager functionality back into PersistenceSession
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/be674cc7
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/be674cc7
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/be674cc7
Branch: refs/heads/master
Commit: be674cc7232e7e3a82f07010a117972c6f288f23
Parents: b8e8cf0
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Sep 14 19:26:27 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Sep 14 19:26:27 2015 +0100
----------------------------------------------------------------------
.../adaptermanager/AdapterManagerDefault.java | 631 +-----------------
.../system/persistence/PersistenceSession.java | 640 ++++++++++++++++++-
.../persistence/PersistenceSessionTest.java | 4 +-
3 files changed, 637 insertions(+), 638 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/be674cc7/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
index 3b1733d..7b68ca9 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/persistence/adaptermanager/AdapterManagerDefault.java
@@ -19,53 +19,22 @@
package org.apache.isis.core.runtime.persistence.adaptermanager;
-import org.datanucleus.enhancement.Persistable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.isis.applib.profiles.Localization;
-import org.apache.isis.core.commons.authentication.AuthenticationSession;
import org.apache.isis.core.commons.components.Resettable;
import org.apache.isis.core.commons.components.SessionScopedComponent;
import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.debug.DebuggableWithTitle;
-import org.apache.isis.core.commons.ensure.Assert;
-import org.apache.isis.core.commons.ensure.Ensure;
-import org.apache.isis.core.commons.ensure.IsisAssertException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.runtimecontext.AdapterManager;
-import org.apache.isis.core.metamodel.runtimecontext.AdapterManagerAware;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
-import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
-import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid;
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.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.facets.actcoll.typeof.ElementSpecificationProviderFromTypeOfFacet;
-import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
-import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
-import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
-import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
-import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
-import org.apache.isis.core.metamodel.spec.ObjectSpecId;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
+import org.apache.isis.core.metamodel.runtimecontext.AdapterManager;
+import org.apache.isis.core.metamodel.runtimecontext.AdapterManagerAware;
import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
-import org.apache.isis.core.runtime.persistence.PojoRecreationException;
-import org.apache.isis.core.runtime.persistence.adapter.PojoAdapter;
-import org.apache.isis.core.runtime.system.context.IsisContext;
-import org.apache.isis.core.runtime.system.persistence.OidGenerator;
import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
-import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-
/**
* Responsible for managing the {@link ObjectAdapter adapter}s and {@link Oid
* identities} for each and every POJO that is being used by the framework.
@@ -88,17 +57,10 @@ public class AdapterManagerDefault implements AdapterManager,
private static final Logger LOG = LoggerFactory.getLogger(AdapterManagerDefault.class);
//region > constructor and fields
- protected final PojoAdapterHashMap pojoAdapterMap = new PojoAdapterHashMap();
- protected final OidAdapterHashMap oidAdapterMap = new OidAdapterHashMap();
private final PersistenceSession persistenceSession;
- private final SpecificationLoaderSpi specificationLoader;
- private final OidMarshaller oidMarshaller;
- private final OidGenerator oidGenerator;
- private final AuthenticationSession authenticationSession;
- private final ServicesInjector servicesInjector;
- private final IsisConfiguration configuration;
- private boolean concurrencyCheckingGloballyEnabled;
+
+ public boolean concurrencyCheckingGloballyEnabled;
// //////////////////////////////////////////////////////////////////
// constructor
@@ -112,19 +74,8 @@ public class AdapterManagerDefault implements AdapterManager,
*/
public AdapterManagerDefault(
final PersistenceSession persistenceSession,
- final SpecificationLoaderSpi specificationLoader,
- final OidMarshaller oidMarshaller,
- final OidGenerator oidGenerator,
- final AuthenticationSession authenticationSession,
- final ServicesInjector servicesInjector,
final IsisConfiguration configuration) {
this.persistenceSession = persistenceSession;
- this.specificationLoader = specificationLoader;
- this.oidMarshaller = oidMarshaller;
- this.oidGenerator = oidGenerator;
- this.authenticationSession = authenticationSession;
- this.servicesInjector = servicesInjector;
- this.configuration = configuration;
final boolean concurrencyCheckingGloballyDisabled =
configuration.getBoolean("isis.persistor.disableConcurrencyChecking", false);
@@ -135,613 +86,98 @@ public class AdapterManagerDefault implements AdapterManager,
//region > open, close
@Override
public void open() {
- oidAdapterMap.open();
- pojoAdapterMap.open();
+ persistenceSession.getOidAdapterMap().open();
+ persistenceSession.getPojoAdapterMap().open();
}
@Override
public void close() {
- oidAdapterMap.close();
- pojoAdapterMap.close();
+ persistenceSession.getOidAdapterMap().close();
+ persistenceSession.getPojoAdapterMap().close();
}
//endregion
//region > reset
@Override
public void reset() {
- oidAdapterMap.reset();
- pojoAdapterMap.reset();
+ persistenceSession.getOidAdapterMap().reset();
+ persistenceSession.getPojoAdapterMap().reset();
}
//endregion
//region > getAdapterFor
@Override
public ObjectAdapter getAdapterFor(final Object pojo) {
- ensureThatArg(pojo, is(notNullValue()));
-
- return pojoAdapterMap.getAdapter(pojo);
+ return persistenceSession.getAdapterFor(pojo);
}
@Override
public ObjectAdapter getAdapterFor(final Oid oid) {
- ensureThatArg(oid, is(notNullValue()));
- ensureMapsConsistent(oid);
-
- return oidAdapterMap.getAdapter(oid);
+ return persistenceSession.getAdapterFor(oid);
}
//endregion
//region > adapterFor
- /**
- * {@inheritDoc}
- */
@Override
public ObjectAdapter adapterFor(final Object pojo) {
-
- if(pojo == null) {
- return null;
- }
- final ObjectAdapter existingOrValueAdapter = existingOrValueAdapter(pojo);
- if(existingOrValueAdapter != null) {
- return existingOrValueAdapter;
- }
-
- final ObjectAdapter newAdapter = createTransientOrViewModelRootAdapter(pojo);
-
- return mapAndInjectServices(newAdapter);
+ return persistenceSession.adapterFor(pojo);
}
-
- /**
- * {@inheritDoc}
- */
@Override
- public ObjectAdapter adapterFor(final Object pojo, final ObjectAdapter parentAdapter, final OneToManyAssociation collection) {
-
- Ensure.ensureThatArg(parentAdapter, is(not(nullValue())));
- Ensure.ensureThatArg(collection, is(not(nullValue())));
-
- final ObjectAdapter existingOrValueAdapter = existingOrValueAdapter(pojo);
- if(existingOrValueAdapter != null) {
- return existingOrValueAdapter;
- }
-
- // the List, Set etc. instance gets wrapped in its own adapter
- final ObjectAdapter newAdapter = createCollectionAdapter(pojo, parentAdapter, collection);
-
- return mapAndInjectServices(newAdapter);
- }
-
- /**
- * Creates an {@link ObjectAdapter adapter} to represent a collection
- * of the parent.
- *
- * <p>
- * The returned adapter will have a {@link ParentedCollectionOid}; its version
- * and its persistence are the same as its owning parent.
- *
- * <p>
- * Should only be called if the pojo is known not to be
- * {@link #getAdapterFor(Object) mapped}.
- */
- private ObjectAdapter createCollectionAdapter(final Object pojo, final ObjectAdapter parentAdapter, final OneToManyAssociation otma) {
-
- ensureMapsConsistent(parentAdapter);
- Assert.assertNotNull(pojo);
-
- final Oid parentOid = parentAdapter.getOid();
-
- // persistence of collection follows the parent
- final ParentedCollectionOid collectionOid = new ParentedCollectionOid((RootOid) parentOid, otma);
- final ObjectAdapter collectionAdapter = createCollectionAdapter(pojo, collectionOid);
-
- // we copy over the type onto the adapter itself
- // [not sure why this is really needed, surely we have enough info in
- // the adapter
- // to look this up on the fly?]
- final TypeOfFacet facet = otma.getFacet(TypeOfFacet.class);
- collectionAdapter.setElementSpecificationProvider(ElementSpecificationProviderFromTypeOfFacet.createFrom(facet));
-
- return collectionAdapter;
+ public ObjectAdapter adapterFor(
+ final Object pojo, final ObjectAdapter parentAdapter, final OneToManyAssociation collection) {
+ return persistenceSession.adapterFor(pojo, parentAdapter, collection);
}
-
@Override
public ObjectAdapter adapterFor(final RootOid rootOid) {
- return adapterFor(rootOid, AdapterManager.ConcurrencyChecking.NO_CHECK);
+ return persistenceSession.adapterFor(rootOid);
}
@Override
public ObjectAdapter adapterFor(
- final RootOid rootOid,
- final AdapterManager.ConcurrencyChecking concurrencyChecking) {
-
- // attempt to locate adapter for the Oid
- ObjectAdapter adapter = getAdapterFor(rootOid);
- if (adapter == null) {
- // else recreate
- try {
- final Object pojo = recreatePojo(rootOid);
- adapter = mapRecreatedPojo(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
- 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 && AdapterManager.ConcurrencyChecking.isCurrentlyEnabled()) {
- LOG.info("concurrency conflict detected on " + recreatedOid + " (" + otherVersion + ")");
- final String currentUser = authenticationSession.getUserName();
- throw new ConcurrencyException(currentUser, recreatedOid, thisVersion, otherVersion);
- } else {
- LOG.warn("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 " + originalOid + " (" + originalVersion + ") to (" + recreatedVersion +")");
- }
- originalOid.setVersion(recreatedVersion);
- }
- }
- }
-
- return adapter;
- }
-
-
- private ObjectAdapter existingOrValueAdapter(Object pojo) {
-
- // attempt to locate adapter for the pojo
- ObjectAdapter adapter = getAdapterFor(pojo);
- if (adapter != null) {
- return adapter;
- }
-
- // pojo may have been lazily loaded by object store, but we haven't yet seen it
- if (pojo instanceof Persistable) {
- adapter = persistenceSession.mapPersistent((Persistable) pojo);
-
- // TODO: could return null if the pojo passed in !dnIsPersistent() || !dnIsDetached()
- // in which case, we would ought to map as a transient object, rather than fall through and treat as a value?
- } else {
- adapter = null;
- }
-
- if(adapter != null) {
- return adapter;
- }
-
- // need to create (and possibly map) the adapter.
- final ObjectSpecification objSpec = specificationLoader.loadSpecification(pojo.getClass());
-
- // we create value facets as standalone (so not added to maps)
- if (objSpec.containsFacet(ValueFacet.class)) {
- adapter = createStandaloneAdapter(pojo);
- return adapter;
- }
-
- return null;
+ final RootOid oid,
+ final ConcurrencyChecking concurrencyChecking) {
+ return persistenceSession.adapterFor(oid, concurrencyChecking);
}
- private Object recreatePojo(RootOid oid) {
- if(oid.isTransient() || oid.isViewModel()) {
- return recreatePojoDefault(oid);
- } else {
- return persistenceSession.loadPojo(oid);
- }
- }
-
- private Object recreatePojoDefault(final RootOid rootOid) {
- final ObjectSpecification spec =
- specificationLoader.lookupBySpecId(rootOid.getObjectSpecId());
- final Object pojo = persistenceSession.instantiateAndInjectServices(spec);
- if(rootOid.isViewModel()) {
- // initialize the view model pojo from the oid's identifier
-
- final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class);
- if(facet == null) {
- throw new IllegalArgumentException("spec does not have RecreatableObjectFacet; " + rootOid.toString() + "; spec is " + spec.getFullIdentifier());
- }
- final String memento = rootOid.getIdentifier();
-
- facet.initialize(pojo, memento);
- }
- return pojo;
- }
//endregion
//region > remapRecreatedPojo
public void remapRecreatedPojo(ObjectAdapter adapter, final Object pojo) {
- removeAdapter(adapter);
- adapter.replacePojo(pojo);
- mapAndInjectServices(adapter);
+ persistenceSession.remapRecreatedPojo(adapter, pojo);
}
//endregion
//region > mapRecreatedPojo
- /**
- * Either returns an existing {@link ObjectAdapter adapter} (as per
- * {@link #getAdapterFor(Object)} or {@link #getAdapterFor(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.
- *
- * @param oid
- * @param recreatedPojo - already known to the object store impl, or a service
- */
+ @Override
public ObjectAdapter mapRecreatedPojo(final Oid oid, final Object recreatedPojo) {
-
- // attempt to locate adapter for the pojo
- // REVIEW: this check is possibly redundant because the pojo will most likely
- // have just been instantiated, so won't yet be in any maps
- final ObjectAdapter adapterLookedUpByPojo = getAdapterFor(recreatedPojo);
- if (adapterLookedUpByPojo != null) {
- return adapterLookedUpByPojo;
- }
-
- // attempt to locate adapter for the Oid
- final ObjectAdapter adapterLookedUpByOid = getAdapterFor(oid);
- if (adapterLookedUpByOid != null) {
- return adapterLookedUpByOid;
- }
-
- final ObjectAdapter createdAdapter = createRootOrAggregatedAdapter(oid, recreatedPojo);
- return mapAndInjectServices(createdAdapter);
+ return persistenceSession.mapRecreatedPojo(oid, recreatedPojo);
}
- private ObjectAdapter createRootOrAggregatedAdapter(final Oid oid, final Object pojo) {
- final ObjectAdapter createdAdapter;
- if(oid instanceof RootOid) {
- final RootOid rootOid = (RootOid) oid;
- createdAdapter = createRootAdapter(pojo, rootOid);
- } else /*if (oid instanceof CollectionOid)*/ {
- final ParentedCollectionOid collectionOid = (ParentedCollectionOid) oid;
- createdAdapter = createCollectionAdapter(pojo, collectionOid);
- }
- return createdAdapter;
- }
//endregion
//region > removeAdapter
- /**
- * Removes the specified object from both the identity-adapter map, and the
- * pojo-adapter map.
- *
- * <p>
- * This indicates that the object is no longer in use, and therefore that no
- * objects exists within the system.
- *
- * <p>
- * If an {@link ObjectAdapter adapter} is removed while its pojo still is
- * referenced then a subsequent interaction of that pojo will create a
- * different {@link ObjectAdapter adapter}.
- *
- * <p>
- * TODO: should do a cascade remove of any aggregated objects.
- */
@Override
public void removeAdapter(final ObjectAdapter adapter) {
- ensureMapsConsistent(adapter);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("removing adapter: " + adapter);
- }
-
- unmap(adapter);
- }
- //endregion
-
- //region > remapAsPersistent
- /**
- * {@inheritDoc}
- *
- * <p>
- * Note that there is no management of {@link Version}s here. That is
- * because the {@link PersistenceSession} is expected to manage this.
- *
- * @param hintRootOid - allow a different persistent root oid to be provided.
- */
- public void remapAsPersistent(final ObjectAdapter adapter, RootOid hintRootOid) {
-
- final ObjectAdapter rootAdapter = adapter.getAggregateRoot(); // TODO: REVIEW: think this is redundant; would seem this method is only ever called for roots anyway.
- final RootOid transientRootOid = (RootOid) rootAdapter.getOid();
-
- // no longer true, because isTransient now looks directly at the underlying pojo's state (for entities)
- // and doesn't apply for services.
-// Ensure.ensureThatArg(rootAdapter.isTransient(), is(true), "root adapter should be transient; oid:" + transientRootOid);
-// Ensure.ensureThatArg(transientRootOid.isTransient(), is(true), "root adapter's OID should be transient; oid:" + transientRootOid);
-
- final RootAndCollectionAdapters rootAndCollectionAdapters = new RootAndCollectionAdapters(adapter, this);
-
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("remapAsPersistent: " + transientRootOid);
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("removing root adapter from oid map");
- }
-
- boolean removed = oidAdapterMap.remove(transientRootOid);
- if (!removed) {
- LOG.warn("could not remove oid: " + transientRootOid);
- // should we fail here with a more serious error?
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("removing collection adapter(s) from oid map");
- }
- for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) {
- final Oid collectionOid = collectionAdapter.getOid();
- removed = oidAdapterMap.remove(collectionOid);
- if (!removed) {
- LOG.warn("could not remove collectionOid: " + collectionOid);
- // should we fail here with a more serious error?
- }
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("updating the Oid");
- }
-
- // intended for testing (bit nasty)
- final RootOid persistedRootOid;
- if(hintRootOid != null) {
- if(hintRootOid.isTransient()) {
- throw new IsisAssertException("hintRootOid must be persistent");
- }
- final ObjectSpecId hintRootOidObjectSpecId = hintRootOid.getObjectSpecId();
- final ObjectSpecId adapterObjectSpecId = adapter.getSpecification().getSpecId();
- if(!hintRootOidObjectSpecId.equals(adapterObjectSpecId)) {
- throw new IsisAssertException("hintRootOid's objectType must be same as that of adapter " +
- "(was: '" + hintRootOidObjectSpecId + "'; adapter's is " + adapterObjectSpecId + "'");
- }
- // ok
- persistedRootOid = hintRootOid;
- } else {
- // normal flow - delegate to OidGenerator to obtain a persistent root oid
- persistedRootOid = oidGenerator.createPersistentOrViewModelOid(adapter.getObject());
- }
-
- // associate root adapter with the new Oid, and remap
- if (LOG.isDebugEnabled()) {
- LOG.debug("replacing Oid for root adapter and re-adding into maps; oid is now: " + persistedRootOid.enString(
- oidMarshaller) + " (was: " + transientRootOid.enString(oidMarshaller) + ")");
- }
- adapter.replaceOid(persistedRootOid);
- oidAdapterMap.add(persistedRootOid, adapter);
-
- // associate the collection adapters with new Oids, and re-map
- if (LOG.isDebugEnabled()) {
- LOG.debug("replacing Oids for collection adapter(s) and re-adding into maps");
- }
- for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) {
- final ParentedCollectionOid previousCollectionOid = (ParentedCollectionOid) collectionAdapter.getOid();
- final ParentedCollectionOid persistedCollectionOid = previousCollectionOid.asPersistent(persistedRootOid);
- oidAdapterMap.add(persistedCollectionOid, collectionAdapter);
- }
-
-
- // some object store implementations may replace collection instances (eg ORM may replace with a cglib-enhanced
- // proxy equivalent. So, ensure that the collection adapters still wrap the correct pojos.
- if (LOG.isDebugEnabled()) {
- LOG.debug("synchronizing collection pojos, remapping in pojo map if required");
- }
- for (final OneToManyAssociation otma : rootAndCollectionAdapters.getCollections()) {
- final ObjectAdapter collectionAdapter = rootAndCollectionAdapters.getCollectionAdapter(otma);
-
- final Object collectionPojoWrappedByAdapter = collectionAdapter.getObject();
- final Object collectionPojoActuallyOnPojo = getCollectionPojo(otma, adapter);
-
- if (collectionPojoActuallyOnPojo != collectionPojoWrappedByAdapter) {
- pojoAdapterMap.remove(collectionAdapter);
- collectionAdapter.replacePojo(collectionPojoActuallyOnPojo);
- pojoAdapterMap.add(collectionPojoActuallyOnPojo, collectionAdapter);
- }
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("made persistent " + adapter + "; was " + transientRootOid);
- }
- }
-
- private static Object getCollectionPojo(final OneToManyAssociation association, final ObjectAdapter ownerAdapter) {
- final PropertyOrCollectionAccessorFacet accessor = association.getFacet(PropertyOrCollectionAccessorFacet.class);
- return accessor.getProperty(ownerAdapter, InteractionInitiatedBy.FRAMEWORK);
+ persistenceSession.removeAdapter(adapter);
}
//endregion
- //region > Helpers: createXxxAdapter
- /**
- * Creates a new transient root {@link ObjectAdapter adapter} for the supplied domain
- * object.
- */
- private ObjectAdapter createTransientOrViewModelRootAdapter(final Object pojo) {
- final RootOid rootOid = oidGenerator.createTransientOrViewModelOid(pojo);
- return createRootAdapter(pojo, rootOid);
- }
-
- /**
- * Creates a {@link ObjectAdapter adapter} with no {@link Oid}.
- *
- * <p>
- * Standalone adapters are never {@link #mapAndInjectServices(ObjectAdapter) mapped}
- * (they have no {@link Oid}, after all).
- *
- * <p>
- * Should only be called if the pojo is known not to be
- * {@link #getAdapterFor(Object) mapped}, and for immutable value types
- * referenced.
- */
- private ObjectAdapter createStandaloneAdapter(final Object pojo) {
- return createAdapter(pojo, null);
- }
-
- /**
- * Creates (but does not {@link #mapAndInjectServices(ObjectAdapter) map}) a new
- * root {@link ObjectAdapter adapter} for the supplied domain object.
- *
- * @see #createStandaloneAdapter(Object)
- * @see #createCollectionAdapter(Object, ParentedCollectionOid)
- */
- private ObjectAdapter createRootAdapter(final Object pojo, RootOid rootOid) {
- Ensure.ensureThatArg(rootOid, is(not(nullValue())));
- return createAdapter(pojo, rootOid);
- }
-
- private ObjectAdapter createCollectionAdapter(
- final Object pojo,
- ParentedCollectionOid collectionOid) {
- Ensure.ensureThatArg(collectionOid, is(not(nullValue())));
- return createAdapter(pojo, collectionOid);
- }
-
- private PojoAdapter createAdapter(
- final Object pojo,
- final Oid oid) {
- return new PojoAdapter(
- pojo, oid,
- authenticationSession, getLocalization(),
- specificationLoader, persistenceSession);
- }
-
- //endregion
-
- //region > Helpers: mapAndInjectServices & unmap
- private ObjectAdapter mapAndInjectServices(final ObjectAdapter adapter) {
- // 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, pojoAdapterMap.containsPojo(pojo));
-
- if (LOG.isDebugEnabled()) {
- // don't interact with the underlying object because may be a ghost
- // and would trigger a resolve
- // don't call toString() on adapter because calls hashCode on
- // underlying object, may also trigger a resolve.
- LOG.debug("adding identity for adapter with oid=" + adapter.getOid());
- }
-
- // value adapters are not mapped (but all others - root and aggregated adapters - are)
- if (adapter.isValue()) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("not mapping value adapter");
- }
- servicesInjector.injectServicesInto(pojo);
- return adapter;
- }
-
- // add all aggregated collections
- final ObjectSpecification objSpec = adapter.getSpecification();
- if (!adapter.isParentedCollection() || adapter.isParentedCollection() && !objSpec.isImmutable()) {
- pojoAdapterMap.add(pojo, adapter);
- }
-
- // order is important - add to pojo map first, then identity map
- oidAdapterMap.add(adapter.getOid(), adapter);
-
- // must inject after mapping, otherwise infinite loop
- servicesInjector.injectServicesInto(pojo);
+ //region > remapAsPersistent
- return adapter;
+ public void remapAsPersistent(final ObjectAdapter adapter, RootOid hintRootOid) {
+ persistenceSession.remapAsPersistent(adapter, hintRootOid);
}
- private void unmap(final ObjectAdapter adapter) {
- ensureMapsConsistent(adapter);
-
- final Oid oid = adapter.getOid();
- if (oid != null) {
- oidAdapterMap.remove(oid);
- }
- pojoAdapterMap.remove(adapter);
- }
//endregion
- //region > Helpers: ensure invariants
- /**
- * Fail early if any problems.
- */
- private void ensureMapsConsistent(final ObjectAdapter adapter) {
- if (adapter.isValue()) {
- return;
- }
- if (adapter.isParentedCollection()) {
- return;
- }
- ensurePojoAdapterMapConsistent(adapter);
- ensureOidAdapterMapConsistent(adapter);
- }
-
- /**
- * Fail early if any problems.
- */
- private void ensureMapsConsistent(final Oid oid) {
- ensureThatArg(oid, is(notNullValue()));
-
- final ObjectAdapter adapter = oidAdapterMap.getAdapter(oid);
- if (adapter == null) {
- return;
- }
- ensureOidAdapterMapConsistent(adapter);
- ensurePojoAdapterMapConsistent(adapter);
- }
-
- private void ensurePojoAdapterMapConsistent(final ObjectAdapter adapter) {
- final Object adapterPojo = adapter.getObject();
- final ObjectAdapter adapterAccordingToPojoAdapterMap = pojoAdapterMap.getAdapter(adapterPojo);
- // take care not to touch the pojo, since it might have been deleted.
- ensureThatArg(
- adapter, is(adapterAccordingToPojoAdapterMap),
- "mismatch in PojoAdapterMap: provided adapter's OID: " + adapter.getOid() + "; \n" + " but map's adapter's OID was : " + adapterAccordingToPojoAdapterMap.getOid());
- }
-
- private void ensureOidAdapterMapConsistent(final ObjectAdapter adapter) {
- final Oid adapterOid = adapter.getOid();
- final ObjectAdapter adapterAccordingToOidAdapterMap = oidAdapterMap.getAdapter(adapterOid);
- // take care not to touch the pojo, since it might have been deleted.
- ensureThatArg(
- adapter, is(adapterAccordingToOidAdapterMap),
- "mismatch in OidAdapter map: " + "adapter's Oid: " + adapterOid + ", " + "provided adapter's OID: "
- + adapter.getOid() + "; " + "map's adapter's Oid: " + adapterAccordingToOidAdapterMap.getOid());
- }
- //endregion
//region > debug
@Override
@@ -751,12 +187,12 @@ public class AdapterManagerDefault implements AdapterManager,
@Override
public void debugData(final DebugBuilder debug) {
- debug.appendTitle(pojoAdapterMap.debugTitle());
- pojoAdapterMap.debugData(debug);
+ debug.appendTitle(persistenceSession.getPojoAdapterMap().debugTitle());
+ persistenceSession.getPojoAdapterMap().debugData(debug);
debug.appendln();
- debug.appendTitle(oidAdapterMap.debugTitle());
- oidAdapterMap.debugData(debug);
+ debug.appendTitle(persistenceSession.getOidAdapterMap().debugTitle());
+ persistenceSession.getOidAdapterMap().debugData(debug);
}
//endregion
@@ -772,11 +208,6 @@ public class AdapterManagerDefault implements AdapterManager,
}
//endregion
- //region > dependencies (from context)
- protected Localization getLocalization() {
- return IsisContext.getLocalization();
- }
- //endregion
}
http://git-wip-us.apache.org/repos/asf/isis/blob/be674cc7/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java
----------------------------------------------------------------------
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 f0ee6ac..f537d7e 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
@@ -37,6 +37,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.isis.applib.RecoverableException;
+import org.apache.isis.applib.profiles.Localization;
import org.apache.isis.applib.query.Query;
import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
@@ -48,23 +49,20 @@ import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.debug.DebuggableWithTitle;
import org.apache.isis.core.commons.ensure.Assert;
+import org.apache.isis.core.commons.ensure.Ensure;
+import org.apache.isis.core.commons.ensure.IsisAssertException;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.commons.util.ToString;
-import org.apache.isis.core.metamodel.runtimecontext.ConfigurationService;
-import org.apache.isis.core.metamodel.runtimecontext.ConfigurationServiceAware;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
-import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionServiceAware;
-import org.apache.isis.core.metamodel.runtimecontext.MessageBrokerService;
-import org.apache.isis.core.metamodel.runtimecontext.MessageBrokerServiceAware;
-import org.apache.isis.core.metamodel.runtimecontext.AdapterManager;
-import org.apache.isis.core.metamodel.runtimecontext.AdapterManagerAware;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid;
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.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.ElementSpecificationProviderFromTypeOfFacet;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet;
import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedCallbackFacet;
@@ -74,7 +72,17 @@ import org.apache.isis.core.metamodel.facets.object.callbacks.PersistingCallback
import org.apache.isis.core.metamodel.facets.object.callbacks.RemovingCallbackFacet;
import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatedCallbackFacet;
import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatingCallbackFacet;
+import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
+import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.runtimecontext.AdapterManager;
+import org.apache.isis.core.metamodel.runtimecontext.AdapterManagerAware;
+import org.apache.isis.core.metamodel.runtimecontext.ConfigurationService;
+import org.apache.isis.core.metamodel.runtimecontext.ConfigurationServiceAware;
+import org.apache.isis.core.metamodel.runtimecontext.MessageBrokerService;
+import org.apache.isis.core.metamodel.runtimecontext.MessageBrokerServiceAware;
+import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionService;
+import org.apache.isis.core.metamodel.runtimecontext.PersistenceSessionServiceAware;
import org.apache.isis.core.metamodel.services.ServiceUtil;
import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
import org.apache.isis.core.metamodel.services.container.query.QueryCardinality;
@@ -88,9 +96,14 @@ import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.isis.core.runtime.persistence.FixturesInstalledFlag;
import org.apache.isis.core.runtime.persistence.NotPersistableException;
import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
+import org.apache.isis.core.runtime.persistence.PojoRecreationException;
import org.apache.isis.core.runtime.persistence.PojoRefreshException;
import org.apache.isis.core.runtime.persistence.UnsupportedFindException;
+import org.apache.isis.core.runtime.persistence.adapter.PojoAdapter;
import org.apache.isis.core.runtime.persistence.adaptermanager.AdapterManagerDefault;
+import org.apache.isis.core.runtime.persistence.adaptermanager.OidAdapterHashMap;
+import org.apache.isis.core.runtime.persistence.adaptermanager.PojoAdapterHashMap;
+import org.apache.isis.core.runtime.persistence.adaptermanager.RootAndCollectionAdapters;
import org.apache.isis.core.runtime.persistence.container.DomainObjectContainerResolve;
import org.apache.isis.core.runtime.persistence.internal.RuntimeContextFromSession;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
@@ -197,8 +210,8 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
this.oidGenerator = new OidGenerator(this, specificationLoader);
- this.adapterManager = new AdapterManagerDefault(this, specificationLoader, oidMarshaller,
- oidGenerator, authenticationSession, servicesInjector, configuration);
+ this.adapterManager = new AdapterManagerDefault(this,
+ configuration);
this.persistenceQueryFactory = new PersistenceQueryFactory(getSpecificationLoader(), adapterManager);
this.transactionManager = new IsisTransactionManager(this, servicesInjector);
@@ -278,7 +291,7 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
? getAdapterManager().adapterFor(service)
: getAdapterManager().mapRecreatedPojo(existingOid, service);
if (serviceAdapter.getOid().isTransient()) {
- adapterManager.remapAsPersistent(serviceAdapter, null);
+ remapAsPersistent(serviceAdapter, null);
}
if (existingOid == null) {
@@ -477,7 +490,7 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
facet.initialize(pojo, memento);
}
- final ObjectAdapter adapter = adapterManager.adapterFor(pojo);
+ final ObjectAdapter adapter = adapterFor(pojo);
return initializePropertiesAndDoCallback(adapter);
}
@@ -742,7 +755,7 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
// could be refactored to use getAdapterManager().adapterFor(...)
ensureThatArg(oid, is(notNullValue()));
- final ObjectAdapter adapter = adapterManager.getAdapterFor(oid);
+ final ObjectAdapter adapter = getAdapterFor(oid);
if (adapter != null) {
return adapter;
}
@@ -756,7 +769,7 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
}
final Object pojo = loadPojo(oid);
- return adapterManager.mapRecreatedPojo(oid, pojo);
+ return mapRecreatedPojo(oid, pojo);
}
});
}
@@ -1132,9 +1145,6 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
debug.appendTitle(getClass().getName());
debug.appendln();
- adapterManager.debugData(debug);
- debug.appendln();
-
debug.appendTitle("OID Generator");
oidGenerator.debugData(debug);
debug.appendln();
@@ -1242,57 +1252,617 @@ public class PersistenceSession implements TransactionalResource, SessionScopedC
// endregion
//region > AdapterManager implementation
- @Override
- public ObjectAdapter getAdapterFor(final Oid oid) {
- return adapterManager.getAdapterFor(oid);
+
+ private final PojoAdapterHashMap pojoAdapterMap = new PojoAdapterHashMap();
+ private final OidAdapterHashMap oidAdapterMap = new OidAdapterHashMap();
+
+ public PojoAdapterHashMap getPojoAdapterMap() {
+ return pojoAdapterMap;
+ }
+
+ public OidAdapterHashMap getOidAdapterMap() {
+ return oidAdapterMap;
}
@Override
public ObjectAdapter getAdapterFor(final Object pojo) {
- return adapterManager.getAdapterFor(pojo);
+ ensureThatArg(pojo, is(notNullValue()));
+
+ return getPojoAdapterMap().getAdapter(pojo);
+ }
+
+ @Override
+ public ObjectAdapter getAdapterFor(final Oid oid) {
+ ensureThatArg(oid, is(notNullValue()));
+ ensureMapsConsistent(oid);
+
+ return getOidAdapterMap().getAdapter(oid);
+ }
+
+
+ public ObjectAdapter existingOrValueAdapter(Object pojo) {
+
+ // attempt to locate adapter for the pojo
+ ObjectAdapter adapter = getAdapterFor(pojo);
+ if (adapter != null) {
+ return adapter;
+ }
+
+ // pojo may have been lazily loaded by object store, but we haven't yet seen it
+ if (pojo instanceof Persistable) {
+ adapter = mapPersistent((Persistable) pojo);
+
+ // TODO: could return null if the pojo passed in !dnIsPersistent() || !dnIsDetached()
+ // in which case, we would ought to map as a transient object, rather than fall through and treat as a value?
+ } else {
+ adapter = null;
+ }
+
+ if(adapter != null) {
+ return adapter;
+ }
+
+ // need to create (and possibly map) the adapter.
+ final ObjectSpecification objSpec = specificationLoader.loadSpecification(pojo.getClass());
+
+ // we create value facets as standalone (so not added to maps)
+ if (objSpec.containsFacet(ValueFacet.class)) {
+ adapter = createStandaloneAdapter(pojo);
+ return adapter;
+ }
+
+ return null;
+ }
+
+
+
+ /**
+ * Fail early if any problems.
+ */
+ public void ensureMapsConsistent(final ObjectAdapter adapter) {
+ if (adapter.isValue()) {
+ return;
+ }
+ if (adapter.isParentedCollection()) {
+ return;
+ }
+ ensurePojoAdapterMapConsistent(adapter);
+ ensureOidAdapterMapConsistent(adapter);
+ }
+
+ /**
+ * Fail early if any problems.
+ */
+ private void ensureMapsConsistent(final Oid oid) {
+ ensureThatArg(oid, is(notNullValue()));
+
+ final ObjectAdapter adapter = getOidAdapterMap().getAdapter(oid);
+ if (adapter == null) {
+ return;
+ }
+ ensureOidAdapterMapConsistent(adapter);
+ ensurePojoAdapterMapConsistent(adapter);
}
+ private void ensurePojoAdapterMapConsistent(final ObjectAdapter adapter) {
+ final Object adapterPojo = adapter.getObject();
+ final ObjectAdapter adapterAccordingToPojoAdapterMap = getPojoAdapterMap().getAdapter(adapterPojo);
+ // take care not to touch the pojo, since it might have been deleted.
+ ensureThatArg(
+ adapter, is(adapterAccordingToPojoAdapterMap),
+ "mismatch in PojoAdapterMap: provided adapter's OID: " + adapter.getOid() + "; \n"
+ + " but map's adapter's OID was : " + adapterAccordingToPojoAdapterMap.getOid());
+ }
+
+ private void ensureOidAdapterMapConsistent(final ObjectAdapter adapter) {
+ final Oid adapterOid = adapter.getOid();
+ final ObjectAdapter adapterAccordingToOidAdapterMap = getOidAdapterMap()
+ .getAdapter(adapterOid);
+ // take care not to touch the pojo, since it might have been deleted.
+ ensureThatArg(
+ adapter, is(adapterAccordingToOidAdapterMap),
+ "mismatch in OidAdapter map: " + "adapter's Oid: " + adapterOid + ", " + "provided adapter's OID: "
+ + adapter.getOid() + "; " + "map's adapter's Oid: " + adapterAccordingToOidAdapterMap.getOid());
+ }
+
+
@Override
- public ObjectAdapter adapterFor(final RootOid oid) {
- return adapterManager.adapterFor(oid);
+ public ObjectAdapter adapterFor(final RootOid rootOid) {
+ return adapterFor(rootOid, AdapterManager.ConcurrencyChecking.NO_CHECK);
}
+
@Override
public ObjectAdapter adapterFor(
- final RootOid oid,
- final ConcurrencyChecking concurrencyChecking) {
- return adapterManager.adapterFor(oid, concurrencyChecking);
+ final RootOid rootOid,
+ final AdapterManager.ConcurrencyChecking concurrencyChecking) {
+
+ // attempt to locate adapter for the Oid
+ ObjectAdapter adapter = getAdapterFor(rootOid);
+ if (adapter == null) {
+ // else recreate
+ try {
+ final Object pojo = recreatePojo(rootOid);
+ adapter = mapRecreatedPojo(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
+ 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(adapterManager.concurrencyCheckingGloballyEnabled && AdapterManager.ConcurrencyChecking.isCurrentlyEnabled()) {
+ LOG.info("concurrency conflict detected on " + recreatedOid + " (" + otherVersion + ")");
+ final String currentUser = authenticationSession.getUserName();
+ throw new ConcurrencyException(currentUser, recreatedOid, thisVersion, otherVersion);
+ } else {
+ LOG.warn("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 " + originalOid + " (" + originalVersion + ") to (" + recreatedVersion +")");
+ }
+ originalOid.setVersion(recreatedVersion);
+ }
+ }
+ }
+
+ return adapter;
+ }
+
+
+ public Object recreatePojo(RootOid oid) {
+ if(oid.isTransient() || oid.isViewModel()) {
+ return recreatePojoDefault(oid);
+ } else {
+ return loadPojo(oid);
+ }
+ }
+
+ private Object recreatePojoDefault(final RootOid rootOid) {
+ final ObjectSpecification spec =
+ specificationLoader.lookupBySpecId(rootOid.getObjectSpecId());
+ final Object pojo = instantiateAndInjectServices(spec);
+ if(rootOid.isViewModel()) {
+ // initialize the view model pojo from the oid's identifier
+
+ final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class);
+ if(facet == null) {
+ throw new IllegalArgumentException("spec does not have RecreatableObjectFacet; " + rootOid.toString() + "; spec is " + spec.getFullIdentifier());
+ }
+
+ final String memento = rootOid.getIdentifier();
+
+ facet.initialize(pojo, memento);
+ }
+ return pojo;
}
+
+
+
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public ObjectAdapter adapterFor(final Object pojo) {
- return adapterManager.adapterFor(pojo);
+
+ if(pojo == null) {
+ return null;
+ }
+ final ObjectAdapter existingOrValueAdapter = existingOrValueAdapter(pojo);
+ if(existingOrValueAdapter != null) {
+ return existingOrValueAdapter;
+ }
+
+ final ObjectAdapter newAdapter = createTransientOrViewModelRootAdapter(pojo);
+
+ return mapAndInjectServices(newAdapter);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public ObjectAdapter adapterFor(
- final Object pojo, final ObjectAdapter parentAdapter, final OneToManyAssociation collection) {
- return adapterManager.adapterFor(pojo, parentAdapter, collection);
+ public ObjectAdapter adapterFor(final Object pojo, final ObjectAdapter parentAdapter, final OneToManyAssociation collection) {
+
+ Ensure.ensureThatArg(parentAdapter, is(not(nullValue())));
+ Ensure.ensureThatArg(collection, is(not(nullValue())));
+
+ final ObjectAdapter existingOrValueAdapter = existingOrValueAdapter(pojo);
+ if(existingOrValueAdapter != null) {
+ return existingOrValueAdapter;
+ }
+
+ // the List, Set etc. instance gets wrapped in its own adapter
+ final ObjectAdapter newAdapter = createCollectionAdapter(pojo, parentAdapter, collection);
+
+ return mapAndInjectServices(newAdapter);
+ }
+
+ /**
+ * Creates an {@link ObjectAdapter adapter} to represent a collection
+ * of the parent.
+ *
+ * <p>
+ * The returned adapter will have a {@link ParentedCollectionOid}; its version
+ * and its persistence are the same as its owning parent.
+ *
+ * <p>
+ * Should only be called if the pojo is known not to be
+ * {@link #getAdapterFor(Object) mapped}.
+ */
+ public ObjectAdapter createCollectionAdapter(
+ final Object pojo,
+ final ObjectAdapter parentAdapter,
+ final OneToManyAssociation otma) {
+
+ ensureMapsConsistent(parentAdapter);
+ Assert.assertNotNull(pojo);
+
+ final Oid parentOid = parentAdapter.getOid();
+
+ // persistence of collection follows the parent
+ final ParentedCollectionOid collectionOid = new ParentedCollectionOid((RootOid) parentOid, otma);
+ final ObjectAdapter collectionAdapter = createCollectionAdapter(pojo, collectionOid);
+
+ // we copy over the type onto the adapter itself
+ // [not sure why this is really needed, surely we have enough info in
+ // the adapter
+ // to look this up on the fly?]
+ final TypeOfFacet facet = otma.getFacet(TypeOfFacet.class);
+ collectionAdapter.setElementSpecificationProvider(ElementSpecificationProviderFromTypeOfFacet.createFrom(facet));
+
+ return collectionAdapter;
}
+
+
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>
+ * Note that there is no management of {@link Version}s here. That is
+ * because the {@link PersistenceSession} is expected to manage this.
+ *
+ * @param hintRootOid - allow a different persistent root oid to be provided.
+ */
public void remapAsPersistent(final ObjectAdapter adapter, RootOid hintRootOid) {
- adapterManager.remapAsPersistent(adapter, hintRootOid);
+
+ final ObjectAdapter rootAdapter = adapter.getAggregateRoot(); // TODO: REVIEW: think this is redundant; would seem this method is only ever called for roots anyway.
+ final RootOid transientRootOid = (RootOid) rootAdapter.getOid();
+
+ // no longer true, because isTransient now looks directly at the underlying pojo's state (for entities)
+ // and doesn't apply for services.
+ // Ensure.ensureThatArg(rootAdapter.isTransient(), is(true), "root adapter should be transient; oid:" + transientRootOid);
+ // Ensure.ensureThatArg(transientRootOid.isTransient(), is(true), "root adapter's OID should be transient; oid:" + transientRootOid);
+
+ final RootAndCollectionAdapters rootAndCollectionAdapters = new RootAndCollectionAdapters(adapter, this);
+
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("remapAsPersistent: " + transientRootOid);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removing root adapter from oid map");
+ }
+
+ boolean removed = getOidAdapterMap().remove(transientRootOid);
+ if (!removed) {
+ LOG.warn("could not remove oid: " + transientRootOid);
+ // should we fail here with a more serious error?
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removing collection adapter(s) from oid map");
+ }
+ for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) {
+ final Oid collectionOid = collectionAdapter.getOid();
+ removed = getOidAdapterMap().remove(collectionOid);
+ if (!removed) {
+ LOG.warn("could not remove collectionOid: " + collectionOid);
+ // should we fail here with a more serious error?
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("updating the Oid");
+ }
+
+ // intended for testing (bit nasty)
+ final RootOid persistedRootOid;
+ if(hintRootOid != null) {
+ if(hintRootOid.isTransient()) {
+ throw new IsisAssertException("hintRootOid must be persistent");
+ }
+ final ObjectSpecId hintRootOidObjectSpecId = hintRootOid.getObjectSpecId();
+ final ObjectSpecId adapterObjectSpecId = adapter.getSpecification().getSpecId();
+ if(!hintRootOidObjectSpecId.equals(adapterObjectSpecId)) {
+ throw new IsisAssertException("hintRootOid's objectType must be same as that of adapter " +
+ "(was: '" + hintRootOidObjectSpecId + "'; adapter's is " + adapterObjectSpecId + "'");
+ }
+ // ok
+ persistedRootOid = hintRootOid;
+ } else {
+ // normal flow - delegate to OidGenerator to obtain a persistent root oid
+ persistedRootOid = oidGenerator.createPersistentOrViewModelOid(adapter.getObject());
+ }
+
+ // associate root adapter with the new Oid, and remap
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("replacing Oid for root adapter and re-adding into maps; oid is now: " + persistedRootOid.enString(
+ oidMarshaller) + " (was: " + transientRootOid.enString(oidMarshaller) + ")");
+ }
+ adapter.replaceOid(persistedRootOid);
+ getOidAdapterMap().add(persistedRootOid, adapter);
+
+ // associate the collection adapters with new Oids, and re-map
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("replacing Oids for collection adapter(s) and re-adding into maps");
+ }
+ for (final ObjectAdapter collectionAdapter : rootAndCollectionAdapters) {
+ final ParentedCollectionOid previousCollectionOid = (ParentedCollectionOid) collectionAdapter.getOid();
+ final ParentedCollectionOid persistedCollectionOid = previousCollectionOid.asPersistent(persistedRootOid);
+ getOidAdapterMap().add(persistedCollectionOid, collectionAdapter);
+ }
+
+
+ // some object store implementations may replace collection instances (eg ORM may replace with a cglib-enhanced
+ // proxy equivalent. So, ensure that the collection adapters still wrap the correct pojos.
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("synchronizing collection pojos, remapping in pojo map if required");
+ }
+ for (final OneToManyAssociation otma : rootAndCollectionAdapters.getCollections()) {
+ final ObjectAdapter collectionAdapter = rootAndCollectionAdapters.getCollectionAdapter(otma);
+
+ final Object collectionPojoWrappedByAdapter = collectionAdapter.getObject();
+ final Object collectionPojoActuallyOnPojo = getCollectionPojo(otma, adapter);
+
+ if (collectionPojoActuallyOnPojo != collectionPojoWrappedByAdapter) {
+ getPojoAdapterMap().remove(collectionAdapter);
+ collectionAdapter.replacePojo(collectionPojoActuallyOnPojo);
+ getPojoAdapterMap().add(collectionPojoActuallyOnPojo, collectionAdapter);
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("made persistent " + adapter + "; was " + transientRootOid);
+ }
}
+ public static Object getCollectionPojo(final OneToManyAssociation association, final ObjectAdapter ownerAdapter) {
+ final PropertyOrCollectionAccessorFacet accessor = association.getFacet(PropertyOrCollectionAccessorFacet.class);
+ return accessor.getProperty(ownerAdapter, InteractionInitiatedBy.FRAMEWORK);
+ }
+
+
+
+ /**
+ * Either returns an existing {@link ObjectAdapter adapter} (as per
+ * {@link #getAdapterFor(Object)} or {@link #getAdapterFor(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.
+ *
+ * @param oid
+ * @param recreatedPojo - already known to the object store impl, or a service
+ */
@Override
public ObjectAdapter mapRecreatedPojo(final Oid oid, final Object recreatedPojo) {
- return adapterManager.mapRecreatedPojo(oid, recreatedPojo);
+
+ // attempt to locate adapter for the pojo
+ // REVIEW: this check is possibly redundant because the pojo will most likely
+ // have just been instantiated, so won't yet be in any maps
+ final ObjectAdapter adapterLookedUpByPojo = getAdapterFor(recreatedPojo);
+ if (adapterLookedUpByPojo != null) {
+ return adapterLookedUpByPojo;
+ }
+
+ // attempt to locate adapter for the Oid
+ final ObjectAdapter adapterLookedUpByOid = getAdapterFor(oid);
+ if (adapterLookedUpByOid != null) {
+ return adapterLookedUpByOid;
+ }
+
+ final ObjectAdapter createdAdapter = createRootOrAggregatedAdapter(oid, recreatedPojo);
+ return mapAndInjectServices(createdAdapter);
}
+ /**
+ * Removes the specified object from both the identity-adapter map, and the
+ * pojo-adapter map.
+ *
+ * <p>
+ * This indicates that the object is no longer in use, and therefore that no
+ * objects exists within the system.
+ *
+ * <p>
+ * If an {@link ObjectAdapter adapter} is removed while its pojo still is
+ * referenced then a subsequent interaction of that pojo will create a
+ * different {@link ObjectAdapter adapter}.
+ *
+ * <p>
+ * TODO: should do a cascade remove of any aggregated objects.
+ */
@Override
public void removeAdapter(final ObjectAdapter adapter) {
- adapterManager.removeAdapter(adapter);
+ ensureMapsConsistent(adapter);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removing adapter: " + adapter);
+ }
+
+ unmap(adapter);
}
+
public void remapRecreatedPojo(ObjectAdapter adapter, final Object pojo) {
- adapterManager.remapRecreatedPojo(adapter, pojo);
+ removeAdapter(adapter);
+ adapter.replacePojo(pojo);
+ mapAndInjectServices(adapter);
}
- // endregion
+
+ public ObjectAdapter createRootOrAggregatedAdapter(final Oid oid, final Object pojo) {
+ final ObjectAdapter createdAdapter;
+ if(oid instanceof RootOid) {
+ final RootOid rootOid = (RootOid) oid;
+ createdAdapter = createRootAdapter(pojo, rootOid);
+ } else /*if (oid instanceof CollectionOid)*/ {
+ final ParentedCollectionOid collectionOid = (ParentedCollectionOid) oid;
+ createdAdapter = createCollectionAdapter(pojo, collectionOid);
+ }
+ return createdAdapter;
+ }
+
+ /**
+ * Creates a new transient root {@link ObjectAdapter adapter} for the supplied domain
+ * object.
+ */
+ public ObjectAdapter createTransientOrViewModelRootAdapter(final Object pojo) {
+ final RootOid rootOid = oidGenerator.createTransientOrViewModelOid(pojo);
+ return createRootAdapter(pojo, rootOid);
+ }
+
+ /**
+ * Creates a {@link ObjectAdapter adapter} with no {@link Oid}.
+ *
+ * <p>
+ * Standalone adapters are never {@link #mapAndInjectServices(ObjectAdapter) mapped}
+ * (they have no {@link Oid}, after all).
+ *
+ * <p>
+ * Should only be called if the pojo is known not to be
+ * {@link #getAdapterFor(Object) mapped}, and for immutable value types
+ * referenced.
+ */
+ public ObjectAdapter createStandaloneAdapter(final Object pojo) {
+ return createAdapter(pojo, null);
+ }
+
+ /**
+ * Creates (but does not {@link #mapAndInjectServices(ObjectAdapter) map}) a new
+ * root {@link ObjectAdapter adapter} for the supplied domain object.
+ *
+ * @see #createStandaloneAdapter(Object)
+ * @see #createCollectionAdapter(Object, ParentedCollectionOid)
+ */
+ public ObjectAdapter createRootAdapter(final Object pojo, RootOid rootOid) {
+ Ensure.ensureThatArg(rootOid, is(not(nullValue())));
+ return createAdapter(pojo, rootOid);
+ }
+
+ public ObjectAdapter createCollectionAdapter(
+ final Object pojo,
+ ParentedCollectionOid collectionOid) {
+ Ensure.ensureThatArg(collectionOid, is(not(nullValue())));
+ return createAdapter(pojo, collectionOid);
+ }
+
+ private PojoAdapter createAdapter(
+ final Object pojo,
+ final Oid oid) {
+ return new PojoAdapter(
+ pojo, oid,
+ authenticationSession, getLocalization(),
+ specificationLoader, this);
+ }
+
+
+ public ObjectAdapter mapAndInjectServices(final ObjectAdapter adapter) {
+ // 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, getPojoAdapterMap().containsPojo(pojo));
+
+ if (LOG.isDebugEnabled()) {
+ // don't interact with the underlying object because may be a ghost
+ // and would trigger a resolve
+ // don't call toString() on adapter because calls hashCode on
+ // underlying object, may also trigger a resolve.
+ LOG.debug("adding identity for adapter with oid=" + adapter.getOid());
+ }
+
+ // value adapters are not mapped (but all others - root and aggregated adapters - are)
+ if (adapter.isValue()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("not mapping value adapter");
+ }
+ servicesInjector.injectServicesInto(pojo);
+ return adapter;
+ }
+
+ // add all aggregated collections
+ final ObjectSpecification objSpec = adapter.getSpecification();
+ if (!adapter.isParentedCollection() || adapter.isParentedCollection() && !objSpec.isImmutable()) {
+ getPojoAdapterMap().add(pojo, adapter);
+ }
+
+ // order is important - add to pojo map first, then identity map
+ getOidAdapterMap().add(adapter.getOid(), adapter);
+
+ // must inject after mapping, otherwise infinite loop
+ servicesInjector.injectServicesInto(pojo);
+
+ return adapter;
+ }
+
+ public void unmap(final ObjectAdapter adapter) {
+ ensureMapsConsistent(adapter);
+
+ final Oid oid = adapter.getOid();
+ if (oid != null) {
+ getOidAdapterMap().remove(oid);
+ }
+ getPojoAdapterMap().remove(adapter);
+ }
+
+
+ //endregion
+
+ //region > dependencies (from context)
+ protected Localization getLocalization() {
+ return IsisContext.getLocalization();
+ }
+
+ //endregion
+
//region > TransactionManager delegate methods
protected IsisTransaction getCurrentTransaction() {
http://git-wip-us.apache.org/repos/asf/isis/blob/be674cc7/core/runtime/src/test/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionTest.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionTest.java
index 040203a..541033a 100644
--- a/core/runtime/src/test/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionTest.java
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionTest.java
@@ -34,7 +34,6 @@ import org.apache.isis.core.commons.authentication.AuthenticationSession;
import org.apache.isis.core.commons.authentication.MessageBroker;
import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
import org.apache.isis.core.metamodel.adapter.version.Version;
import org.apache.isis.core.metamodel.app.IsisMetaModel;
import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
@@ -155,8 +154,7 @@ public class PersistenceSessionTest {
}
};
- adapterManager = new AdapterManagerDefault(persistenceSession, mockSpecificationLoader,
- new OidMarshaller(), mockOidGenerator, mockAuthenticationSession, servicesInjector,
+ adapterManager = new AdapterManagerDefault(persistenceSession,
mockConfiguration);
context.checking(new Expectations(){{