Package org.apache.isis.core.objectstore

Source Code of org.apache.isis.core.objectstore.InMemoryObjectStore

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/

package org.apache.isis.core.objectstore;

import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import com.google.common.collect.Lists;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.commons.debug.DebugUtils;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.adapter.oid.TypedOid;
import org.apache.isis.core.metamodel.adapter.version.Version;
import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.SpecificationLoader;
import org.apache.isis.core.metamodel.spec.feature.Contributed;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.objectstore.commands.InMemoryCreateObjectCommand;
import org.apache.isis.core.objectstore.commands.InMemoryDestroyObjectCommand;
import org.apache.isis.core.objectstore.commands.InMemorySaveObjectCommand;
import org.apache.isis.core.objectstore.internal.ObjectStoreInstances;
import org.apache.isis.core.objectstore.internal.ObjectStorePersistedObjects;
import org.apache.isis.core.objectstore.internal.ObjectStorePersistedObjectsDefault;
import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
import org.apache.isis.core.runtime.persistence.ObjectPersistenceException;
import org.apache.isis.core.runtime.persistence.UnsupportedFindException;
import org.apache.isis.core.runtime.persistence.objectstore.ObjectStoreSpi;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.CreateObjectCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.DestroyObjectCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.PersistenceCommand;
import org.apache.isis.core.runtime.persistence.objectstore.transaction.SaveObjectCommand;
import org.apache.isis.core.runtime.persistence.query.PersistenceQueryBuiltIn;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.core.runtime.system.persistence.PersistenceQuery;
import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
import org.apache.isis.core.runtime.system.persistence.PersistenceSessionFactory;

public class InMemoryObjectStore implements ObjectStoreSpi {

    private final static Logger LOG = LoggerFactory.getLogger(InMemoryObjectStore.class);

    protected ObjectStorePersistedObjects persistedObjects;

    public InMemoryObjectStore() {
        LOG.info("creating memory object store");
    }

    // ///////////////////////////////////////////////////////
    // Name
    // ///////////////////////////////////////////////////////

    @Override
    public String name() {
        return "In-Memory Object Store";
    }

    // ///////////////////////////////////////////////////////
    // open, close, shutdown
    // ///////////////////////////////////////////////////////

    @Override
    public void open() {
        // TODO: all a bit hacky, but is to keep tests running. Should really
        // sort out using mocks.
        final InMemoryPersistenceSessionFactory inMemoryPersistenceSessionFactory = getInMemoryPersistenceSessionFactory();
        persistedObjects = inMemoryPersistenceSessionFactory == null ? null : inMemoryPersistenceSessionFactory.getPersistedObjects();
        if (persistedObjects == null) {
            if (inMemoryPersistenceSessionFactory != null) {
                persistedObjects = inMemoryPersistenceSessionFactory.createPersistedObjects();
            } else {
                persistedObjects = new ObjectStorePersistedObjectsDefault();
            }
        } else {
            recreateAdapters();
        }
    }

    protected void recreateAdapters() {
        for (final ObjectSpecification noSpec : persistedObjects.specifications()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("recreating adapters for: " + noSpec.getFullIdentifier());
            }
            recreateAdapters(persistedObjects.instancesFor(noSpec));
        }
    }

    private void recreateAdapters(final ObjectStoreInstances objectStoreInstances) {
        for (final Oid oid : objectStoreInstances.getOids()) {

            // it's important not to "touch" the pojo, not even in log messages.
            // That's because
            // the toString() will cause bytecode enhancement to try to resolve
            // references.

            if (LOG.isDebugEnabled()) {
                LOG.debug("recreating adapter: oid=" + oid);
            }
            final Object pojo = objectStoreInstances.getPojo(oid);

            final ObjectAdapter existingAdapterLookedUpByPojo = getAdapterManager().getAdapterFor(pojo);
            if (existingAdapterLookedUpByPojo != null) {
                // this could happen if we rehydrate a persisted object that
                // depends on another persisted object
                // not yet rehydrated.
                getPersistenceSession().removeAdapter(existingAdapterLookedUpByPojo);
            }

            final ObjectAdapter existingAdapterLookedUpByOid = getAdapterManager().getAdapterFor(oid);
            if (existingAdapterLookedUpByOid != null) {
                throw new IsisException("A mapping already exists for " + oid + ": " + existingAdapterLookedUpByOid);
            }

            final ObjectAdapter recreatedAdapter = getPersistenceSession().mapRecreatedPojo(oid, pojo);

            final Version version = objectStoreInstances.getVersion(oid);
            recreatedAdapter.setVersion(version);
        }
    }

    @Override
    public void close() {
        final InMemoryPersistenceSessionFactory inMemoryPersistenceSessionFactory = getInMemoryPersistenceSessionFactory();
        // TODO: this is hacky, only here to keep tests running. Should sort out
        // using mocks
        if (inMemoryPersistenceSessionFactory != null) {
            inMemoryPersistenceSessionFactory.attach(getPersistenceSession(), persistedObjects);
            persistedObjects = null;
        }
    }

    // ///////////////////////////////////////////////////////
    // fixtures
    // ///////////////////////////////////////////////////////

    /**
     * No permanent persistence, so must always install fixtures.
     */
    @Override
    public boolean isFixturesInstalled() {
        return false;
    }

    // ///////////////////////////////////////////////////////
    // reset
    // ///////////////////////////////////////////////////////

    @Override
    public void reset() {
    }

    // ///////////////////////////////////////////////////////
    // Transaction management
    // ///////////////////////////////////////////////////////

    @Override
    public void startTransaction() {
    }

    @Override
    public void endTransaction() {
    }

    @Override
    public void abortTransaction() {
    }

    // ///////////////////////////////////////////////////////
    // Command Creation
    // ///////////////////////////////////////////////////////

    @Override
    public CreateObjectCommand createCreateObjectCommand(final ObjectAdapter object) {
        if (object.getSpecification().isParented()) {
            return null;
        }
        return new InMemoryCreateObjectCommand(object, persistedObjects);
    }

    @Override
    public SaveObjectCommand createSaveObjectCommand(final ObjectAdapter object) {
        if (object.getSpecification().isParented()) {
            return null;
        }
        return new InMemorySaveObjectCommand(object, persistedObjects);
    }

    @Override
    public DestroyObjectCommand createDestroyObjectCommand(final ObjectAdapter object) {
        return new InMemoryDestroyObjectCommand(object, persistedObjects);
    }

    // ///////////////////////////////////////////////////////
    // Command Execution
    // ///////////////////////////////////////////////////////

    @Override
    public void execute(final List<PersistenceCommand> commands) throws ObjectPersistenceException {
        if (LOG.isInfoEnabled()) {
            LOG.info("execute commands");
        }
        for (final PersistenceCommand command : commands) {
            command.execute(null);
        }
        LOG.info("end execution");
    }

    // ///////////////////////////////////////////////////////
    // getObject, resolveField, resolveImmediately
    // ///////////////////////////////////////////////////////

    @Override
    public ObjectAdapter loadInstanceAndAdapt(final TypedOid oid) throws ObjectNotFoundException, ObjectPersistenceException {
        if(LOG.isDebugEnabled()) {
            LOG.debug("getObject " + oid);
        }
        final ObjectSpecification objectSpec = getSpecificationLookup().lookupBySpecId(oid.getObjectSpecId());
        final ObjectStoreInstances ins = instancesFor(objectSpec);
        final ObjectAdapter adapter = ins.getObjectAndMapIfRequired(oid);
        if (adapter == null) {
            throw new ObjectNotFoundException(oid);
        }
        return adapter;
    }

    @Override
    public void resolveImmediately(final ObjectAdapter adapter) throws ObjectPersistenceException {

        // these diagnostics are because, even though this method is called by
        // PersistenceSessionObjectStore#resolveImmediately which has a check,
        // seem to be hitting a race condition with another thread that is
        // resolving the object before I get here.
        if (adapter.canTransitionToResolving()) {
            if(LOG.isDebugEnabled()) {
                LOG.debug("resolve " + adapter);
            }
        } else {
            LOG.warn("resolveImmediately ignored, " + "adapter's current state is: " + adapter.getResolveState() + " ; oid: " + adapter.getOid());
        }
       
        adapter.markAsResolvedIfPossible();
    }

    @Override
    public void resolveField(final ObjectAdapter object, final ObjectAssociation field) throws ObjectPersistenceException {
        final ObjectAdapter referenceAdapter = field.get(object);
        referenceAdapter.markAsResolvedIfPossible();
    }



    // ///////////////////////////////////////////////////////
    // getInstances, hasInstances
    // ///////////////////////////////////////////////////////

    @Override
    public List<ObjectAdapter> loadInstancesAndAdapt(final PersistenceQuery persistenceQuery) throws ObjectPersistenceException, UnsupportedFindException {

        if (!(persistenceQuery instanceof PersistenceQueryBuiltIn)) {
            throw new IllegalArgumentException(MessageFormat.format("Provided PersistenceQuery not supported; was {0}; " + "the in-memory object store only supports {1}", persistenceQuery.getClass().getName(), PersistenceQueryBuiltIn.class.getName()));
        }
        final PersistenceQueryBuiltIn builtIn = (PersistenceQueryBuiltIn) persistenceQuery;

        final List<ObjectAdapter> instances = Lists.newArrayList();
        final ObjectSpecification spec = persistenceQuery.getSpecification();
        findInstances(spec, builtIn, instances);
        return resolved(instances);
    }

    @Override
    public boolean hasInstances(final ObjectSpecification spec) {
        if (instancesFor(spec).hasInstances()) {
            return true;
        }

        // includeSubclasses
        final List<ObjectSpecification> subclasses = spec.subclasses();
        for (int i = 0; i < subclasses.size(); i++) {
            if (hasInstances(subclasses.get(i))) {
                return true;
            }
        }

        return false;
    }

    private void findInstances(final ObjectSpecification spec, final PersistenceQueryBuiltIn persistenceQuery, final List<ObjectAdapter> foundInstances) {

        instancesFor(spec).findInstancesAndAdd(persistenceQuery, foundInstances);

        // include subclasses
        final List<ObjectSpecification> subclasses = spec.subclasses();
        for (int i = 0; i < subclasses.size(); i++) {
            findInstances(subclasses.get(i), persistenceQuery, foundInstances);
        }
    }

    private static List<ObjectAdapter> resolved(final List<ObjectAdapter> instances) {
        for (ObjectAdapter adapter: instances) {
            adapter.markAsResolvedIfPossible();
        }
        return instances;
    }

    // ///////////////////////////////////////////////////////
    // Services
    // ///////////////////////////////////////////////////////

    @Override
    public RootOid getOidForService(ObjectSpecification serviceSpec) {
        return (RootOid) persistedObjects.getService(serviceSpec.getSpecId());
    }

    @Override
    public void registerService(final RootOid rootOid) {
        persistedObjects.registerService(rootOid.getObjectSpecId(), rootOid);
    }

    private ObjectStoreInstances instancesFor(final ObjectSpecification spec) {
        return persistedObjects.instancesFor(spec);
    }

    // ///////////////////////////////////////////////////////
    // Debugging
    // ///////////////////////////////////////////////////////

    @Override
    public String debugTitle() {
        return name();
    }

    @Override
    public void debugData(final DebugBuilder debug) {
        debug.appendTitle("Domain Objects");
        for (final ObjectSpecification spec : persistedObjects.specifications()) {
            debug.appendln(spec.getFullIdentifier());
            final ObjectStoreInstances instances = instancesFor(spec);
            instances.debugData(debug);
        }
        debug.unindent();
        debug.appendln();
    }

    private String debugCollectionGraph(final ObjectAdapter collection, final int level, final Vector<ObjectAdapter> recursiveElements) {
        final StringBuffer s = new StringBuffer();

        if (recursiveElements.contains(collection)) {
            s.append("*\n");
        } else {
            recursiveElements.addElement(collection);

            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            final Iterator<ObjectAdapter> e = facet.iterator(collection);

            while (e.hasNext()) {
                indent(s, level);

                ObjectAdapter element;
                try {
                    element = e.next();
                } catch (final ClassCastException ex) {
                    LOG.error(ex.getMessage(), ex);
                    return s.toString();
                }

                s.append(element);
                s.append(debugGraph(element, level + 1, recursiveElements));
            }
        }

        return s.toString();
    }

    private String debugGraph(final ObjectAdapter object, final int level, final Vector<ObjectAdapter> recursiveElements) {
        if (level > 3) {
            return "...\n"; // only go 3 levels?
        }

        Vector<ObjectAdapter> elements;
        if (recursiveElements == null) {
            elements = new Vector<ObjectAdapter>(25, 10);
        } else {
            elements = recursiveElements;
        }

        if (object.getSpecification().isParentedOrFreeCollection()) {
            return "\n" + debugCollectionGraph(object, level, elements);
        } else {
            return "\n" + debugObjectGraph(object, level, elements);
        }
    }

    private String debugObjectGraph(final ObjectAdapter object, final int level, final Vector<ObjectAdapter> recursiveElements) {
        final StringBuffer s = new StringBuffer();

        recursiveElements.addElement(object);

        // work through all its fields
        final List<ObjectAssociation> fields = object.getSpecification().getAssociations(Contributed.EXCLUDED);

        for (int i = 0; i < fields.size(); i++) {
            final ObjectAssociation field = fields.get(i);
            final Object obj = field.get(object);

            final String id = field.getId();
            indent(s, level);

            if (field.isOneToManyAssociation()) {
                s.append(id + ": \n" + debugCollectionGraph((ObjectAdapter) obj, level + 1, recursiveElements));
            } else {
                if (recursiveElements.contains(obj)) {
                    s.append(id + ": " + obj + "*\n");
                } else {
                    s.append(id + ": " + obj);
                    s.append(debugGraph((ObjectAdapter) obj, level + 1, recursiveElements));
                }
            }
        }

        return s.toString();
    }

    private void indent(final StringBuffer s, final int level) {
        for (int indent = 0; indent < level; indent++) {
            s.append(DebugUtils.indentString(4) + "|");
        }

        s.append(DebugUtils.indentString(4) + "+--");
    }

    // ///////////////////////////////////////////////////////
    // Dependencies (from context)
    // ///////////////////////////////////////////////////////

    /**
     * Must use {@link IsisContext context}, because although this object is
     * recreated with each {@link PersistenceSession session}, the persisted
     * objects that get
     * {@link #attachPersistedObjects(ObjectStorePersistedObjects) attached} to
     * it span multiple sessions.
     *
     * <p>
     * The alternative design would be to laboriously inject the session into
     * not only this object but also the {@link ObjectStoreInstances} that do
     * the work.
     */
    protected PersistenceSession getPersistenceSession() {
        return IsisContext.getPersistenceSession();
    }

    /**
     * Must use {@link IsisContext context}, because although this object is
     * recreated with each {@link PersistenceSession session}, the persisted
     * objects that get
     * {@link #attachPersistedObjects(ObjectStorePersistedObjects) attached} to
     * it span multiple sessions.
     *
     * <p>
     * The alternative design would be to laboriously inject the session into
     * not only this object but also the {@link ObjectStoreInstances} that do
     * the work.
     */
    protected AdapterManager getAdapterManager() {
        return getPersistenceSession().getAdapterManager();
    }

    protected SpecificationLoader getSpecificationLookup() {
        return IsisContext.getSpecificationLoader();
    }


    /**
     * Downcasts the {@link PersistenceSessionFactory} to
     * {@link InMemoryPersistenceSessionFactory}.
     */
    protected InMemoryPersistenceSessionFactory getInMemoryPersistenceSessionFactory() {
        final PersistenceSessionFactory persistenceSessionFactory = getPersistenceSession().getPersistenceSessionFactory();

        if (!(persistenceSessionFactory instanceof InMemoryPersistenceSessionFactory)) {
            // for testing support
            return null;
        }
        return (InMemoryPersistenceSessionFactory) persistenceSessionFactory;
    }

   
}
TOP

Related Classes of org.apache.isis.core.objectstore.InMemoryObjectStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.