All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nakedobjects.remoting.client.persistence.PersistenceSessionProxy Maven / Gradle / Ivy

package org.nakedobjects.remoting.client.persistence;

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

import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.ResolveState;
import org.nakedobjects.metamodel.adapter.oid.Oid;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.component.ApplicationScopedComponent;
import org.nakedobjects.metamodel.commons.component.SessionScopedComponent;
import org.nakedobjects.metamodel.commons.debug.DebugString;
import org.nakedobjects.metamodel.facets.collections.modify.CollectionFacet;
import org.nakedobjects.metamodel.services.ServicesInjector;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.remoting.shared.ServerFacade;
import org.nakedobjects.remoting.shared.data.Data;
import org.nakedobjects.remoting.shared.encoding.object.ObjectEncoder;
import org.nakedobjects.remoting.shared.encoding.object.data.IdentityData;
import org.nakedobjects.remoting.shared.encoding.object.data.ObjectData;
import org.nakedobjects.remoting.shared.encoding.query.data.PersistenceQueryData;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.persistence.PersistenceSessionAbstract;
import org.nakedobjects.runtime.persistence.PersistenceSessionFactory;
import org.nakedobjects.runtime.persistence.adapterfactory.AdapterFactory;
import org.nakedobjects.runtime.persistence.adaptermanager.AdapterManagerExtended;
import org.nakedobjects.runtime.persistence.objectfactory.ObjectFactory;
import org.nakedobjects.runtime.persistence.oidgenerator.OidGenerator;
import org.nakedobjects.runtime.persistence.query.PersistenceQuery;
import org.nakedobjects.runtime.persistence.query.PersistenceQueryBuiltIn;
import org.nakedobjects.runtime.transaction.TransactionalClosureAbstract;
import org.nakedobjects.runtime.transaction.TransactionalClosureWithReturnAbstract;
import org.nakedobjects.runtime.transaction.updatenotifier.UpdateNotifier;


public class PersistenceSessionProxy extends PersistenceSessionAbstract {

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

    private ServerFacade connection;
    private ObjectEncoder encoder;

    private final Hashtable cache = new Hashtable();
    private HashMap services = new HashMap();


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

    public PersistenceSessionProxy(
            final PersistenceSessionFactory persistenceSessionFactory,
            final AdapterFactory adapterFactory, 
            final ObjectFactory objectFactory, 
            final ServicesInjector containerInjector, 
            final OidGenerator oidGenerator, 
            final AdapterManagerExtended identityMap, 
            final ServerFacade distribution, 
            final ObjectEncoder encoder) {
        super(persistenceSessionFactory, adapterFactory, objectFactory, containerInjector, oidGenerator, identityMap);
        setConnection(distribution);
        setEncoder(encoder);
    }


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

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

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


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


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

    public void objectChanged(final NakedObject adapter) {
        if (adapter.isTransient()) {
            getUpdateNotifier().addChangedObject(adapter);
        }

        if (adapter.getResolveState().respondToChangesInPersistentObjects()) {
            getClientSideTransactionManager().addObjectChanged(adapter);
        }
    }



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


    public synchronized void destroyObject(final NakedObject object) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("destroyObject " + object);
        }
        getClientSideTransactionManager().addDestroyObject(object);

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

    //////////////////////////////////////////////////////////////////
    // makePersistent
    //////////////////////////////////////////////////////////////////
    
    /**
     * REVIEW: we should perhaps have a little more symmetry here, and
     * have the {@link ServerFacade} callback to the {@link PersistenceSession}
     * (the PersistenceSessionPersist API) to handle remapping
     * of adapters.
     */
    public synchronized void makePersistent(final NakedObject object) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("makePersistent " + object);
        }
        getClientSideTransactionManager().addMakePersistent(object);

        // 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)
        
    }



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


    @Override
    protected NakedObject[] getInstances(final PersistenceQuery persistenceQuery) {
        final NakedObjectSpecification noSpec = persistenceQuery.getSpecification();
        if (LOG.isDebugEnabled()) {
        	LOG.debug("getInstances of " + noSpec + " with " + persistenceQuery);
        }
        
        // REVIEW: really not sure what this is doing
        if (cache.containsKey(noSpec) && persistenceQuery instanceof PersistenceQueryBuiltIn) {
            PersistenceQueryBuiltIn builtIn = (PersistenceQueryBuiltIn) persistenceQuery;
			final NakedObject collection = (NakedObject) cache.get(noSpec);
            if (collection.getSpecification().isCollection()) {
                final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
                final List instances = new ArrayList();
                for (NakedObject instance : facet.iterable(collection)) {
                    if (builtIn.matches(instance)) {
                        instances.add(instance);
                    }
                }
                return (NakedObject[]) instances.toArray(new NakedObject[instances.size()]);
            }
        }

        return findInstancesFromServer(persistenceQuery);
    }


	private NakedObject[] findInstancesFromServer(
			final PersistenceQuery persistenceQuery) {
		final PersistenceQueryData criteriaData = encoder.encodePersistenceQuery(persistenceQuery);
        return getTransactionManager().executeWithinTransaction(
    		new TransactionalClosureWithReturnAbstract(){
				public NakedObject[] execute() {
					final ObjectData[] instancesAsObjectData = connection.findInstances(getAuthenticationSession(), criteriaData);
					final NakedObject[] instances = new NakedObject[instancesAsObjectData.length];
					for (int i = 0; i < instancesAsObjectData.length; i++) {
						instances[i] = encoder.decode(instancesAsObjectData[i]);
					}
					return instances;
				}
				@Override
				public void onSuccess() {
					clearAllDirty();
				}
				});
	}


    public boolean hasInstances(final NakedObjectSpecification specification) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("hasInstances of " + specification);
        }
        if (cache.containsKey(specification)) {
            final NakedObject collection = (NakedObject) cache.get(specification);
            final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
            if (facet != null) {
                return facet.size(collection) > 0;
            }
        }
        return hasInstancesFromServer(specification);
    }


	private boolean hasInstancesFromServer(
			final NakedObjectSpecification specification) {
		return getTransactionManager().executeWithinTransaction(
    		new TransactionalClosureWithReturnAbstract(){
				public Boolean execute() {
					return connection.hasInstances(getAuthenticationSession(), specification.getFullName());
				}});
	}


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

    public synchronized NakedObject loadObject(final Oid oid, final NakedObjectSpecification hint) {
        final NakedObject adapter = getAdapterManager().getAdapterFor(oid);
        if (adapter != null) {
            return adapter;
        }
        return loadObjectFromServer(oid, hint);
    }


	private NakedObject loadObjectFromServer(final Oid oid,
			final NakedObjectSpecification hint) {
		return getTransactionManager().executeWithinTransaction(
        		new TransactionalClosureWithReturnAbstract(){
			public NakedObject execute() {
				final ObjectData data = connection.getObject(null, oid, hint.getFullName());
				return encoder.decode(data);
			}});
	}

    public void reload(final NakedObject object) {
        final IdentityData identityData = encoder.encodeIdentityData(object);
        reloadFromServer(identityData);
    }


	private void reloadFromServer(final IdentityData identityData) {
		getTransactionManager().executeWithinTransaction(
    		new TransactionalClosureAbstract() {
				public void execute() {
					final ObjectData update = connection.resolveImmediately(getAuthenticationSession(), identityData);
					encoder.decode(update);
				}});
	}

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

    public synchronized void resolveImmediately(final NakedObject 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);
    	}
    	
    	resolveImmediatelyFromServer(adapter);
    }

	private void resolveImmediatelyFromServer(final NakedObject adapter) {
		getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract(){
			public void execute() {
				final ObjectData data = connection.resolveImmediately(
						getAuthenticationSession(), encoder.encodeIdentityData(adapter));
				encoder.decode(data);
			}
			});
	}

    public void resolveField(final NakedObject adapter, final NakedObjectAssociation field) {
        if (field.getSpecification().isCollectionOrIsAggregated()) {
            return;
        }
        final NakedObject 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());
        }
        resolveFieldFromServer(adapter, field);
    }


	private void resolveFieldFromServer(final NakedObject adapter,
			final NakedObjectAssociation field) {
		getTransactionManager().executeWithinTransaction(new TransactionalClosureAbstract() {
			public void execute() {
				final Data data = connection.resolveField(getAuthenticationSession(), encoder.encodeIdentityData(adapter), field
						.getId());
				encoder.decode(data);
			}});
	}


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

    @Override
    public Oid getOidForService(final String name) {
        Oid oid = services.get(name);
        if (oid == null) {
            final IdentityData data = connection.oidForService(getAuthenticationSession(), name);
            oid = data.getOid();
            registerService(name, oid);
        }
        return oid;
    }

    @Override
    public void registerService(final String name, final Oid oid) {
        services.put(name, oid);
    }


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

    @Override
    public void debugData(final DebugString debug) {
        super.debugData(debug);
        debug.appendln("Connection", connection);
    }

    public String debugTitle() {
        return "Proxy Object Manager";
    }



    //////////////////////////////////////////////////////////////////
    // Dependencies (injected)
    //////////////////////////////////////////////////////////////////

    public void setCacheInstances(final String[] names) {
        for (int i = 0; i < names.length; i++) {
            final NakedObjectSpecification spec = NakedObjectsContext.getSpecificationLoader().loadSpecification(names[i]);
            cache.put(spec, Boolean.TRUE);
        }
    }


    public void setConnection(final ServerFacade connection) {
        this.connection = connection;
    }

    public void setEncoder(final ObjectEncoder factory) {
        this.encoder = factory;
    }

    
    /**
     * Downcasts.
     */
    private ClientSideTransactionManager getClientSideTransactionManager() {
        return (ClientSideTransactionManager) getTransactionManager();
    }



    //////////////////////////////////////////////////////////////////
    // Dependencies (from context)
    //////////////////////////////////////////////////////////////////
    
    private AuthenticationSession getAuthenticationSession() {
        return NakedObjectsContext.getAuthenticationSession();
    }

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



}
// Copyright (c) Naked Objects Group Ltd.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy