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(){{