Package org.apache.isis.runtimes.dflt.remoting.protocol.internal

Source Code of org.apache.isis.runtimes.dflt.remoting.protocol.internal.ObjectDeserializer

/*
*  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.protocol.internal;

import java.util.Enumeration;

import org.apache.log4j.Logger;

import org.apache.isis.core.commons.exceptions.UnknownTypeException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.ResolveState;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
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.facets.object.encodeable.EncodableFacet;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.Persistability;
import org.apache.isis.core.metamodel.spec.SpecificationLoader;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.isis.runtimes.dflt.remoting.common.IsisRemoteException;
import org.apache.isis.runtimes.dflt.remoting.common.data.Data;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.CollectionData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.EncodableObjectData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.IdentityData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.NullData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.ObjectData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.ReferenceData;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.KnownObjectsRequest;
import org.apache.isis.runtimes.dflt.runtime.persistence.PersistorUtil;
import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.PersistenceSessionObjectStore;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionHydrator;
import org.apache.isis.runtimes.dflt.runtime.system.transaction.UpdateNotifier;

public class ObjectDeserializer {

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

    private final FieldOrderCache fieldOrderCache;

    public ObjectDeserializer(final FieldOrderCache fieldOrderCache) {
        this.fieldOrderCache = fieldOrderCache;
    }

    // ///////////////////////////////////////////////////////
    // restore
    // ///////////////////////////////////////////////////////

    public ObjectAdapter deserialize(final Data data) {
        if (data instanceof CollectionData) {
            return deserializeCollection((CollectionData) data, new KnownObjectsRequest());
        } else {
            return deserializeObject(data, new KnownObjectsRequest());
        }
    }

    public ObjectAdapter deserialize(final Data data, final KnownObjectsRequest knownObjects) {
        if (data instanceof CollectionData) {
            return deserializeCollection((CollectionData) data, knownObjects);
        } else {
            return deserializeObject(data, knownObjects);
        }
    }

    // ///////////////////////////////////////////////////////
    // Helper: restoreCollection
    // ///////////////////////////////////////////////////////

    private ObjectAdapter deserializeCollection(final CollectionData data, final KnownObjectsRequest knownObjects) {
        final String collectionType = data.getType();
        final ObjectSpecification collectionSpecification = getSpecificationLoader().loadSpecification(collectionType);

        /*
         * if we are to deal with internal collections then we need to be able
         * to get the collection from it's parent via its field
         */
        final ObjectAdapter collection = getPersistenceSession().createInstance(collectionSpecification);
        if (data.getElements() == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("restoring empty collection");
            }
            return collection;
        } else {
            final ReferenceData[] elements = data.getElements();
            if (LOG.isDebugEnabled()) {
                LOG.debug("restoring collection " + elements.length + " elements");
            }
            final ObjectAdapter[] initData = new ObjectAdapter[elements.length];
            for (int i = 0; i < elements.length; i++) {
                final ObjectAdapter element = deserializeObject(elements[i], knownObjects);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("restoring collection element :" + element);
                }
                initData[i] = element;
            }
            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            facet.init(collection, initData);
            return collection;
        }
    }

    // ///////////////////////////////////////////////////////
    // Helper: deserializeObject
    // ///////////////////////////////////////////////////////

    private ObjectAdapter deserializeObject(final Data data, final KnownObjectsRequest knownObjects) {
        if (data instanceof NullData) {
            return null;
        } else if (data instanceof ObjectData) {
            final ObjectData objectData = (ObjectData) data;
            return deserializeObjectFromObjectData(objectData, knownObjects);
        } else if (data instanceof IdentityData) {
            final IdentityData identityData = (IdentityData) data;
            return deserializeObjectFromIdentityData(identityData, knownObjects);
        } else if (data instanceof EncodableObjectData) {
            final EncodableObjectData encodableObjectData = (EncodableObjectData) data;
            return deserializeObjectFromEncodableObjectData(encodableObjectData);
        } else {
            throw new UnknownTypeException(data);
        }
    }

    private ObjectAdapter deserializeObjectFromIdentityData(final IdentityData data, final KnownObjectsRequest knownObjects) {
        final Oid oid = data.getOid();
        /*
         * either create a new transient object, get an existing object and
         * update it if data is for resolved object, or create new object and
         * set it
         */
        ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
        if (adapter == null) {
            final ObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());
            adapter = getHydrator().recreateAdapter(oid, specification);
        }
        return adapter;
    }

    private ObjectAdapter deserializeObjectFromObjectData(final ObjectData data, final KnownObjectsRequest knownObjects) {
        if (knownObjects.containsKey(data)) {
            return knownObjects.get(data);
        }

        final Oid oid = data.getOid();
        /*
         * either create a new transient object, get an existing object and
         * update it if data is for resolved object, or create new object and
         * set it
         */
        ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
        if (adapter != null) {
            updateLoadedObject(adapter, data, knownObjects);
        } else if (oid.isTransient()) {
            adapter = deserializeTransient(data, knownObjects);
        } else {
            adapter = deserializePersistentObject(data, oid, knownObjects);
        }

        return adapter;
    }

    private ObjectAdapter deserializeTransient(final ObjectData adapterData, final KnownObjectsRequest knownObjects) {
        final ObjectSpecification specification = getSpecificationLoader().loadSpecification(adapterData.getType());

        final ObjectAdapter adapter = getHydrator().recreateAdapter(adapterData.getOid(), specification);
        if (LOG.isDebugEnabled()) {
            LOG.debug("restore transient object " + adapter);
        }
        knownObjects.put(adapter, adapterData);
        setUpFields(adapter, adapterData, knownObjects);
        return adapter;
    }

    private ObjectAdapter deserializePersistentObject(final ObjectData data, final Oid oid, final KnownObjectsRequest knownObjects) {
        // unknown object; create an instance
        final ObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());

        final ObjectAdapter adapter = getHydrator().recreateAdapter(oid, specification);
        if (data.getFieldContent() != null) {
            adapter.setOptimisticLock(data.getVersion());
            ResolveState state;
            state = data.hasCompleteData() ? ResolveState.RESOLVING : ResolveState.RESOLVING_PART;
            if (LOG.isDebugEnabled()) {
                LOG.debug("restoring existing object (" + state.name() + ") " + adapter);
            }
            setupFields(adapter, data, state, knownObjects);
        }
        return adapter;
    }

    private ObjectAdapter deserializeObjectFromEncodableObjectData(final EncodableObjectData encodeableObjectData) {
        if (encodeableObjectData.getEncodedObjectData() == null) {
            return null;
        }
        final ObjectSpecification spec = getSpecificationLoader().loadSpecification(encodeableObjectData.getType());
        final EncodableFacet encoder = spec.getFacet(EncodableFacet.class);
        final ObjectAdapter adapter = encoder.fromEncodedString(encodeableObjectData.getEncodedObjectData());
        return adapter;
    }

    // ///////////////////////////////////////////////////////
    // Helpers: updateLoadedObject
    // ///////////////////////////////////////////////////////

    private void updateLoadedObject(final ObjectAdapter adapter, final ObjectData adapterData, final KnownObjectsRequest knownObjects) {
        // object known and we have all the latest data; update/resolve the
        // object
        if (adapterData.getFieldContent() != null) {
            adapter.setOptimisticLock(adapterData.getVersion());
            final ResolveState state = nextState(adapter.getResolveState(), adapterData.hasCompleteData());
            if (state != null) {
                LOG.debug("updating existing object (" + state.name() + ") " + adapter);
                setupFields(adapter, adapterData, state, knownObjects);
                getUpdateNotifier().addChangedObject(adapter);
            }
        } else {
            if (adapterData.getVersion() != null && adapterData.getVersion().different(adapter.getVersion())) {
                // TODO reload the object
            }
        }
    }

    // ///////////////////////////////////////////////////////
    // Helpers: setupFields
    // ///////////////////////////////////////////////////////

    private void setUpCollectionField(final ObjectAdapter adapter, final ObjectData parentData, final ObjectAssociation field, final CollectionData collectionContentData, final KnownObjectsRequest knownObjects) {
        if (collectionContentData.hasAllElements()) {
            setUpCollectionFieldForEntireContents(adapter, field, collectionContentData, knownObjects);
        } else {
            setUpCollectionFieldForNoContents(parentData, adapter, field);
        }
    }

    private void setUpCollectionFieldForNoContents(final ObjectData parentData, final ObjectAdapter adapter, final ObjectAssociation field) {
        final ObjectAdapter collection = field.get(adapter);
        if (collection.getResolveState() == ResolveState.GHOST) {
            return;
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("No data for collection: " + field.getId());
        }

        final Version adapterVersion = adapter.getVersion();
        final Version parentVersion = parentData.getVersion();
        if (adapterVersion.different(parentVersion)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("clearing collection as versions differ: " + adapter.getVersion() + " " + parentData.getVersion());
            }
            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            facet.init(collection, new ObjectAdapter[0]);
            collection.changeState(ResolveState.GHOST);
        }
    }

    private void setUpCollectionFieldForEntireContents(final ObjectAdapter adapter, final ObjectAssociation field, final CollectionData collectionContentData, final KnownObjectsRequest knownObjects) {
        final int size = collectionContentData.getElements().length;
        final ObjectAdapter[] elements = new ObjectAdapter[size];
        for (int j = 0; j < elements.length; j++) {
            elements[j] = deserializeObject(collectionContentData.getElements()[j], knownObjects);
            if (LOG.isDebugEnabled()) {
                LOG.debug("adding element to " + field.getId() + ": " + elements[j]);
            }
        }

        final ObjectAdapter col = field.get(adapter);
        final ResolveState initialState = col.getResolveState();
        final ResolveState state = nextState(initialState, collectionContentData.hasAllElements());
        if (state != null) {
            PersistorUtil.start(col, state);
            final ObjectAdapter collection = ((OneToManyAssociation) field).get(adapter);
            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            facet.init(collection, elements);
            PersistorUtil.end(col);
        } else {
            LOG.warn("not initialising collection " + col + " due to current state " + initialState);
        }
    }

    private void setupFields(final ObjectAdapter adapter, final ObjectData adapterData, final ResolveState state, final KnownObjectsRequest knownObjects) {
        if (adapter.getResolveState().isDeserializable(state)) {
            PersistorUtil.start(adapter, state);
            setUpFields(adapter, adapterData, knownObjects);
            PersistorUtil.end(adapter);
        }
    }

    private void setUpFields(final ObjectAdapter adapter, final ObjectData adapterData, final KnownObjectsRequest knownObjects) {
        final Data[] fieldContent = adapterData.getFieldContent();
        if (fieldContent != null && fieldContent.length > 0) {
            final ObjectAssociation[] fields = fieldOrderCache.getFields(adapter.getSpecification());
            if (fields.length != fieldContent.length) {
                throw new IsisRemoteException("Data received for different number of fields; expected " + fields.length + ", but was " + fieldContent.length);
            }
            for (int i = 0; i < fields.length; i++) {
                final ObjectAssociation field = fields[i];
                final Data fieldData = fieldContent[i];
                if (fieldData == null || field.isNotPersisted()) {
                    LOG.debug("no data for field " + field.getId());
                    continue;
                }

                if (field.isOneToManyAssociation()) {
                    setUpCollectionField(adapter, adapterData, field, (CollectionData) fieldData, knownObjects);
                } else if (field.getSpecification().isEncodeable()) {
                    setUpEncodedField(adapter, (OneToOneAssociation) field, fieldData);
                } else {
                    setUpReferenceField(adapter, (OneToOneAssociation) field, fieldData, knownObjects);
                }
            }
        }
    }

    private void setUpReferenceField(final ObjectAdapter adapter, final OneToOneAssociation field, final Data data, final KnownObjectsRequest knownObjects) {
        ObjectAdapter associate;
        associate = deserializeObject(data, knownObjects);
        if (LOG.isDebugEnabled()) {
            LOG.debug("setting association for field " + field.getId() + ": " + associate);
        }
        field.initAssociation(adapter, associate);
    }

    private void setUpEncodedField(final ObjectAdapter adapter, final OneToOneAssociation field, final Data data) {
        String value;
        if (data instanceof NullData) {
            field.initAssociation(adapter, null);
        } else {
            value = ((EncodableObjectData) data).getEncodedObjectData();
            final EncodableFacet encoder = field.getSpecification().getFacet(EncodableFacet.class);
            final ObjectAdapter valueAdapter = encoder.fromEncodedString(value);
            if (LOG.isDebugEnabled()) {
                LOG.debug("setting value for field " + field.getId() + ": " + valueAdapter);
            }
            field.initAssociation(adapter, valueAdapter);
        }
    }

    // ///////////////////////////////////////////////////////
    // madePersistent
    // ///////////////////////////////////////////////////////

    /**
     * Equivalent to
     * {@link PersistenceSessionObjectStore#remapAsPersistent(ObjectAdapter)}.
     */
    public void madePersistent(final ObjectAdapter adapter, final ObjectData adapterData) {
        if (adapterData == null) {
            return;
        }

        if (adapter.isTransient() && adapter.getSpecification().persistability() != Persistability.TRANSIENT) {

            getAdapterManager().getAdapterFor(adapterData.getOid()); // causes
                                                                     // OID to
                                                                     // be
                                                                     // updated
            adapter.setOptimisticLock(adapterData.getVersion());
            adapter.changeState(ResolveState.RESOLVED);
        }

        final Data[] fieldData = adapterData.getFieldContent();
        if (fieldData == null) {
            return;
        }
        final ObjectAssociation[] fields = fieldOrderCache.getFields(adapter.getSpecification());
        for (int i = 0; i < fieldData.length; i++) {
            if (fieldData[i] == null) {
                continue;
            }
            if (fields[i].isOneToOneAssociation()) {
                final ObjectAdapter field = ((OneToOneAssociation) fields[i]).get(adapter);
                final ObjectData fieldContent = (ObjectData) adapterData.getFieldContent()[i];
                if (field != null) {
                    madePersistent(field, fieldContent);
                }
            } else if (fields[i].isOneToManyAssociation()) {
                final CollectionData collectionData = (CollectionData) adapterData.getFieldContent()[i];
                final ObjectAdapter collectionAdapter = fields[i].get(adapter);
                if (!collectionAdapter.isPersistent()) {
                    collectionAdapter.changeState(ResolveState.RESOLVED);
                }
                final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collectionAdapter);
                final Enumeration elements = facet.elements(collectionAdapter);
                for (int j = 0; j < collectionData.getElements().length; j++) {
                    final ObjectAdapter element = (ObjectAdapter) elements.nextElement();
                    if (collectionData.getElements()[j] instanceof ObjectData) {
                        final ObjectData elementData = (ObjectData) collectionData.getElements()[j];
                        madePersistent(element, elementData);
                    }
                }
            }
        }
    }

    // ///////////////////////////////////////////////////////
    // Helpers: nextState
    // ///////////////////////////////////////////////////////

    private ResolveState nextState(final ResolveState initialState, final boolean complete) {
        ResolveState state = null;
        if (initialState == ResolveState.RESOLVED) {
            state = ResolveState.UPDATING;
        } else if (initialState == ResolveState.GHOST || initialState == ResolveState.PART_RESOLVED) {
            state = complete ? ResolveState.RESOLVING : ResolveState.RESOLVING_PART;
        } else if (initialState == ResolveState.TRANSIENT) {
            state = ResolveState.SERIALIZING_TRANSIENT;
        }
        return state;
    }

    // ///////////////////////////////////////////////////////
    // Dependencies (from singletons)
    // ///////////////////////////////////////////////////////

    private SpecificationLoader getSpecificationLoader() {
        return IsisContext.getSpecificationLoader();
    }

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

    private PersistenceSession getPersistenceSession() {
        return IsisContext.getPersistenceSession();
    }

    private AdapterManager getAdapterManager() {
        return getPersistenceSession().getAdapterManager();
    }

    private PersistenceSessionHydrator getHydrator() {
        return getPersistenceSession();
    }

}
TOP

Related Classes of org.apache.isis.runtimes.dflt.remoting.protocol.internal.ObjectDeserializer

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.