Package org.apache.isis.runtimes.dflt.remoting.common.client.persistence

Source Code of org.apache.isis.runtimes.dflt.remoting.common.client.persistence.PersistenceSessionProxy

/*
*  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.runtimes.dflt.remoting.common.client.persistence;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import org.apache.isis.core.commons.authentication.AuthenticationSession;
import org.apache.isis.core.commons.components.ApplicationScopedComponent;
import org.apache.isis.core.commons.components.SessionScopedComponent;
import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory;
import org.apache.isis.core.metamodel.adapter.ResolveState;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
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.services.ServicesInjector;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.runtimes.dflt.remoting.common.data.Data;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.IdentityData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.ObjectData;
import org.apache.isis.runtimes.dflt.remoting.common.data.query.PersistenceQueryData;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.FindInstancesRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.FindInstancesResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.GetObjectRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.GetObjectResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.HasInstancesRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.HasInstancesResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.OidForServiceRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.OidForServiceResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ResolveFieldRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ResolveFieldResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ResolveObjectRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ResolveObjectResponse;
import org.apache.isis.runtimes.dflt.remoting.common.facade.ServerFacade;
import org.apache.isis.runtimes.dflt.remoting.common.protocol.ObjectEncoderDecoder;
import org.apache.isis.runtimes.dflt.runtime.persistence.PersistenceSessionAbstract;
import org.apache.isis.runtimes.dflt.runtime.persistence.adaptermanager.AdapterManagerExtended;
import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.PersistenceSessionObjectStore;
import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryBuiltIn;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.ObjectFactory;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.OidGenerator;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceQuery;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionFactory;
import org.apache.isis.runtimes.dflt.runtime.system.transaction.UpdateNotifier;
import org.apache.isis.runtimes.dflt.runtime.transaction.TransactionalClosureAbstract;
import org.apache.isis.runtimes.dflt.runtime.transaction.TransactionalClosureWithReturnAbstract;

public class PersistenceSessionProxy extends PersistenceSessionAbstract {

    final static Logger LOG = Logger.getLogger(PersistenceSessionProxy.class);

    private final ServerFacade serverFacade;
    private final ObjectEncoderDecoder encoderDecoder;

    /**
     * Keyed on an adapter wrapping a <tt>java.util.List</tt> (or equiv), ie
     * with a {@link CollectionFacet}.
     */
    private final Map<ObjectSpecification, ObjectAdapter> cache = new HashMap<ObjectSpecification, ObjectAdapter>();
    private final HashMap<String, Oid> serviceOidByNameCache = new HashMap<String, Oid>();

    // ////////////////////////////////////////////////////////////////
    // Constructor
    // ////////////////////////////////////////////////////////////////

    public PersistenceSessionProxy(final PersistenceSessionFactory persistenceSessionFactory, final ObjectAdapterFactory adapterFactory, final ObjectFactory objectFactory, final ServicesInjector containerInjector, final OidGenerator oidGenerator, final AdapterManagerExtended adapterManager,
            final ServerFacade serverFacade, final ObjectEncoderDecoder encoder) {
        super(persistenceSessionFactory, adapterFactory, objectFactory, containerInjector, oidGenerator, adapterManager);
        this.serverFacade = serverFacade;
        this.encoderDecoder = encoder;
    }

    // ////////////////////////////////////////////////////////////////
    // init, shutdown, reset, isInitialized
    // ////////////////////////////////////////////////////////////////

    /**
     * TODO: mismatch between {@link SessionScopedComponent} (open) and
     * {@link ApplicationScopedComponent} (init).
     */
    @Override
    public void doOpen() {
        serverFacade.init();
    }

    /**
     * TODO: mismatch between {@link SessionScopedComponent} (open) and
     * {@link ApplicationScopedComponent} (init).
     */
    @Override
    public void doClose() {
        serverFacade.shutdown();
    }

    /**
     * No need to install fixtures, rely on server-side to do the right thing.
     */
    @Override
    public boolean isFixturesInstalled() {
        return true;
    }

    // ////////////////////////////////////////////////////////////////
    // loadObject, reload
    // ////////////////////////////////////////////////////////////////

    @Override
    public synchronized ObjectAdapter loadObject(final Oid oid, final ObjectSpecification hintSpec) {
        final ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
        if (adapter != null) {
            return adapter;
        }
        return loadObjectFromPersistenceLayer(oid, hintSpec);
    }

    private ObjectAdapter loadObjectFromPersistenceLayer(final Oid oid, final ObjectSpecification specHint) {
        return getTransactionManager().executeWithinTransaction(new TransactionalClosureWithReturnAbstract<ObjectAdapter>() {
            @Override
            public ObjectAdapter execute() {
                // NB: I think that the auth session must be null because we may
                // not yet have logged in if
                // retrieving the services.
                final GetObjectRequest request = new GetObjectRequest(null, oid, specHint.getFullIdentifier());
                final GetObjectResponse response = serverFacade.getObject(request);
                final ObjectData data = response.getObjectData();
                return encoderDecoder.decode(data);
            }
        });
    }

    @Override
    public void reload(final ObjectAdapter object) {
        final IdentityData identityData = encoderDecoder.encodeIdentityData(object);
        reloadFromPersistenceLayer(identityData);
    }

    private void reloadFromPersistenceLayer(final IdentityData identityData) {
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {
                final ResolveObjectRequest request = new ResolveObjectRequest(getAuthenticationSession(), identityData);
                final ResolveObjectResponse response = serverFacade.resolveImmediately(request);
                final ObjectData update = response.getObjectData();
                encoderDecoder.decode(update);
            }
        });
    }

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

    @Override
    public synchronized void resolveImmediately(final ObjectAdapter adapter) {
        final ResolveState resolveState = adapter.getResolveState();
        if (!resolveState.canChangeTo(ResolveState.RESOLVING)) {
            return;
        }
        final Oid oid = adapter.getOid();
        if (LOG.isDebugEnabled()) {
            LOG.debug("resolve object (remotely from server)" + oid);
        }

        resolveImmediatelyFromPersistenceLayer(adapter);
    }

    private void resolveImmediatelyFromPersistenceLayer(final ObjectAdapter adapter) {
        final IdentityData adapterData = encoderDecoder.encodeIdentityData(adapter);
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {
                final ResolveObjectRequest request = new ResolveObjectRequest(getAuthenticationSession(), adapterData);
                // unlike the server-side implementation we don't invoke the
                // callbacks
                // for loading and loaded (they will already have been called in
                // the server)
                final ResolveObjectResponse response = serverFacade.resolveImmediately(request);
                final ObjectData data = response.getObjectData();
                encoderDecoder.decode(data);
            }
        });
    }

    @Override
    public void resolveField(final ObjectAdapter adapter, final ObjectAssociation field) {
        if (field.getSpecification().isCollectionOrIsAggregated()) {
            return;
        }
        final ObjectAdapter referenceAdapter = field.get(adapter);
        if (referenceAdapter != null && referenceAdapter.getResolveState().isResolved()) {
            return;
        }
        if (referenceAdapter == null || !referenceAdapter.isPersistent()) {
            return;
        }
        if (LOG.isInfoEnabled()) {
            LOG.info("resolveField on server: " + adapter + "/" + field.getId());
        }
        resolveFieldFromPersistenceLayer(adapter, field);
    }

    private void resolveFieldFromPersistenceLayer(final ObjectAdapter adapter, final ObjectAssociation field) {
        final IdentityData adapterData = encoderDecoder.encodeIdentityData(adapter);
        final String fieldId = field.getId();
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {
                final ResolveFieldRequest request = new ResolveFieldRequest(getAuthenticationSession(), adapterData, fieldId);
                final ResolveFieldResponse response = serverFacade.resolveField(request);
                final Data data = response.getData();
                encoderDecoder.decode(data);
            }
        });
    }

    // ////////////////////////////////////////////////////////////////
    // makePersistent
    // ////////////////////////////////////////////////////////////////

    /**
     * REVIEW: we should perhaps have a little more symmetry here, and have the
     * {@link ServerFacade} callback to the {@link PersistenceSession} (the
     * <tt>PersistenceSessionPersist</tt> API) to handle remapping of adapters.
     */
    @Override
    public synchronized void makePersistent(final ObjectAdapter object) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("makePersistent " + object);
        }
        // TODO: the PSOS has more checks; should we do the same?
        makePersistentInPersistenceLayer(object);
    }

    protected void makePersistentInPersistenceLayer(final ObjectAdapter object) {
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {

                // the two implementations (proxy vs object store) vary here.
                // the object store does not make this call directly, it
                // instead delegates to the PersistAlgorithm that makes a
                // callback to the PersistenceSessionPersist API,
                // which in turn calls remaps the adapters.
                //
                // the proxy persistor on the other hand does nothing here.
                // instead we remap the adapter in distribution code,
                // processing the handling of the returned results.
                //
                // (see REVIEW comment above)

                getTransactionManager().addMakePersistent(object);
            }
        });
    }

    // ////////////////////////////////////////////////////////////////
    // objectChanged
    // ////////////////////////////////////////////////////////////////

    @Override
    public void objectChanged(final ObjectAdapter adapter) {
        if (adapter.isTransient()) {
            addObjectChangedToPresentationLayer(adapter);
            return;
        }
        if (adapter.getResolveState().respondToChangesInPersistentObjects()) {
            if (isImmutable(adapter)) {
                return;
            }
            addObjectChangedToPersistenceLayer(adapter);
        }
    }

    private void addObjectChangedToPresentationLayer(final ObjectAdapter adapter) {
        getUpdateNotifier().addChangedObject(adapter);
    }

    private void addObjectChangedToPersistenceLayer(final ObjectAdapter adapter) {
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {
                getTransactionManager().addObjectChanged(adapter);
            }
        });
    }

    // ////////////////////////////////////////////////////////////////
    // destroy
    // ////////////////////////////////////////////////////////////////

    @Override
    public synchronized void destroyObject(final ObjectAdapter object) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("destroyObject " + object);
        }
        destroyObjectInPersistenceLayer(object);

        // TODO need to do garbage collection instead
        // Isis.getObjectLoader().unloaded(object);
    }

    protected void destroyObjectInPersistenceLayer(final ObjectAdapter object) {
        getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
            @Override
            public void execute() {
                getTransactionManager().addDestroyObject(object);
            }
        });
    }

    // ////////////////////////////////////////////////////////////////
    // getInstances
    // ////////////////////////////////////////////////////////////////

    @Override
    protected ObjectAdapter[] getInstances(final PersistenceQuery persistenceQuery) {
        final ObjectSpecification noSpec = persistenceQuery.getSpecification();
        if (LOG.isDebugEnabled()) {
            LOG.debug("getInstances of " + noSpec + " with " + persistenceQuery);
        }

        if (canSatisfyFromCache(persistenceQuery)) {
            return getInstancesFromCache(persistenceQuery);
        }

        return findInstancesFromPersistenceLayer(persistenceQuery);
    }

    private boolean canSatisfyFromCache(final PersistenceQuery persistenceQuery) {
        final ObjectSpecification noSpec = persistenceQuery.getSpecification();
        return cache.containsKey(noSpec) && persistenceQuery instanceof PersistenceQueryBuiltIn;
    }

    /**
     * TODO: this code is not currently in use because there is no way to set up
     * the cache. We may want to change what the cache is keyed on.
     */
    private ObjectAdapter[] getInstancesFromCache(final PersistenceQuery persistenceQuery) {
        final ObjectSpecification noSpec = persistenceQuery.getSpecification();
        final PersistenceQueryBuiltIn builtIn = (PersistenceQueryBuiltIn) persistenceQuery;
        final ObjectAdapter collection = cache.get(noSpec);
        if (!collection.getSpecification().isCollection()) {
            return new ObjectAdapter[0];
        }

        final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
        final List<ObjectAdapter> instances = new ArrayList<ObjectAdapter>();
        for (final ObjectAdapter instance : facet.iterable(collection)) {
            if (builtIn.matches(instance)) {
                instances.add(instance);
            }
        }
        return instances.toArray(new ObjectAdapter[instances.size()]);
    }

    private ObjectAdapter[] findInstancesFromPersistenceLayer(final PersistenceQuery persistenceQuery) {
        final PersistenceQueryData criteriaData = encoderDecoder.encodePersistenceQuery(persistenceQuery);
        return getTransactionManager().executeWithinTransaction(new TransactionalClosureWithReturnAbstract<ObjectAdapter[]>() {
            @Override
            public ObjectAdapter[] execute() {
                final FindInstancesRequest request = new FindInstancesRequest(getAuthenticationSession(), criteriaData);
                final FindInstancesResponse response = serverFacade.findInstances(request);
                final ObjectData[] instancesAsObjectData = response.getInstances();
                final ObjectAdapter[] instances = new ObjectAdapter[instancesAsObjectData.length];
                for (int i = 0; i < instancesAsObjectData.length; i++) {
                    instances[i] = encoderDecoder.decode(instancesAsObjectData[i]);
                }
                return instances;
            }

            @Override
            public void onSuccess() {
                clearAllDirty();
            }
        });
    }

    // ////////////////////////////////////////////////////////////////
    // hasInstances
    // ////////////////////////////////////////////////////////////////

    @Override
    public boolean hasInstances(final ObjectSpecification specification) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("hasInstances of " + specification);
        }
        if (cache.containsKey(specification)) {
            return hasInstancesFromCache(specification);
        }
        return hasInstancesFromPersistenceLayer(specification);
    }

    private boolean hasInstancesFromCache(final ObjectSpecification specification) {
        final ObjectAdapter collection = cache.get(specification);
        final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
        if (facet == null) {
            return false;
        }
        return facet.size(collection) > 0;
    }

    private boolean hasInstancesFromPersistenceLayer(final ObjectSpecification specification) {
        final String specFullName = specification.getFullIdentifier();
        return getTransactionManager().executeWithinTransaction(new TransactionalClosureWithReturnAbstract<Boolean>() {
            @Override
            public Boolean execute() {
                final HasInstancesRequest request = new HasInstancesRequest(getAuthenticationSession(), specFullName);
                final HasInstancesResponse response = serverFacade.hasInstances(request);
                return response.hasInstances();
            }
        });
    }

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

    /**
     * TODO: not symmetric with {@link PersistenceSessionObjectStore}; we have a
     * cache but it does not?
     */
    @Override
    public Oid getOidForService(final String name) {
        Oid oid = serviceOidByNameCache.get(name);
        if (oid == null) {
            oid = getOidForServiceFromPersistenceLayer(name);
            registerService(name, oid);
        }
        return oid;
    }

    private Oid getOidForServiceFromPersistenceLayer(final String serviceId) {
        final OidForServiceRequest request = new OidForServiceRequest(getAuthenticationSession(), serviceId);
        final OidForServiceResponse response = serverFacade.oidForService(request);
        final IdentityData data = response.getOidData();
        return data.getOid();
    }

    /**
     * TODO: not symmetric with {@link PersistenceSessionObjectStore}; we have a
     * cache but it does not?
     */
    @Override
    public void registerService(final String name, final Oid oid) {
        serviceOidByNameCache.put(name, oid);
    }

    // ///////////////////////////////////////////////////////////////////////////
    // TransactionManager
    // ///////////////////////////////////////////////////////////////////////////

    /**
     * Just downcasts.
     */
    @Override
    public ClientSideTransactionManager getTransactionManager() {
        return (ClientSideTransactionManager) super.getTransactionManager();
    }

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

    @Override
    public void debugData(final DebugBuilder debug) {
        super.debugData(debug);
        debug.appendln("Server Facade", serverFacade);
    }

    @Override
    public String debugTitle() {
        return "Proxy Persistence Sessino";
    }

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

    private static AuthenticationSession getAuthenticationSession() {
        return IsisContext.getAuthenticationSession();
    }

    private static UpdateNotifier getUpdateNotifier() {
        return IsisContext.getUpdateNotifier();
    }

}
TOP

Related Classes of org.apache.isis.runtimes.dflt.remoting.common.client.persistence.PersistenceSessionProxy

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.