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

org.nakedobjects.remoting.protocol.encoding.internal.ObjectEncoderDecoderDefault Maven / Gradle / Ivy

The newest version!
package org.nakedobjects.remoting.protocol.encoding.internal;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

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.adapter.version.Version;
import org.nakedobjects.metamodel.commons.ensure.Assert;
import org.nakedobjects.metamodel.commons.exceptions.UnknownTypeException;
import org.nakedobjects.metamodel.commons.factory.InstanceFactory;
import org.nakedobjects.metamodel.config.NakedObjectConfiguration;
import org.nakedobjects.metamodel.facets.collections.modify.CollectionFacet;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.remoting.NakedObjectsRemoteException;
import org.nakedobjects.remoting.client.facets.ActionInvocationFacetWrapProxy;
import org.nakedobjects.remoting.client.facets.PropertySetterFacetWrapProxy;
import org.nakedobjects.remoting.client.persistence.ClientSideTransactionManager;
import org.nakedobjects.remoting.client.persistence.PersistenceSessionProxy;
import org.nakedobjects.remoting.data.Data;
import org.nakedobjects.remoting.data.DataFactory;
import org.nakedobjects.remoting.data.DataFactoryDefault;
import org.nakedobjects.remoting.data.common.CollectionData;
import org.nakedobjects.remoting.data.common.EncodableObjectData;
import org.nakedobjects.remoting.data.common.IdentityData;
import org.nakedobjects.remoting.data.common.NullData;
import org.nakedobjects.remoting.data.common.ObjectData;
import org.nakedobjects.remoting.data.common.ReferenceData;
import org.nakedobjects.remoting.data.query.PersistenceQueryData;
import org.nakedobjects.remoting.exchange.AuthorizationResponse;
import org.nakedobjects.remoting.exchange.ClearAssociationRequest;
import org.nakedobjects.remoting.exchange.ClearValueRequest;
import org.nakedobjects.remoting.exchange.ExecuteClientActionRequest;
import org.nakedobjects.remoting.exchange.ExecuteClientActionResponse;
import org.nakedobjects.remoting.exchange.ExecuteServerActionRequest;
import org.nakedobjects.remoting.exchange.ExecuteServerActionResponse;
import org.nakedobjects.remoting.exchange.FindInstancesRequest;
import org.nakedobjects.remoting.exchange.GetObjectRequest;
import org.nakedobjects.remoting.exchange.KnownObjectsRequest;
import org.nakedobjects.remoting.exchange.ResolveFieldRequest;
import org.nakedobjects.remoting.exchange.ResolveObjectRequest;
import org.nakedobjects.remoting.exchange.SetAssociationRequest;
import org.nakedobjects.remoting.exchange.SetValueRequest;
import org.nakedobjects.remoting.facade.impl.ServerFacadeImpl;
import org.nakedobjects.remoting.protocol.encoding.EncodingProtocolConstants;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistorUtil;
import org.nakedobjects.runtime.persistence.query.PersistenceQuery;

public class ObjectEncoderDecoderDefault implements ObjectEncoderDecoder {
	
	private final static Logger LOG = Logger.getLogger(ObjectEncoderDecoderDefault.class);
	
	public static final int DEFAULT_CLIENT_SIDE_ADD_OBJECT_GRAPH_DEPTH = 1;
	public static final int DEFAULT_CLIENT_SIDE_UPDATE_OBJECT_GRAPH_DEPTH = 1;
	public static final int DEFAULT_CLIENT_SIDE_ACTION_TARGET_GRAPH_DEPTH = 0;
	public static final int DEFAULT_CLIENT_SIDE_ACTION_PARAMETER_GRAPH_DEPTH = 0;
	
	public static final int DEFAULT_SERVER_SIDE_RETRIEVED_OBJECT_GRAPH_DEPTH = 100;
	public static final int DEFAULT_SERVER_SIDE_TOUCHED_OBJECT_GRAPH_DEPTH = 1;

    private final ObjectSerializer serializer;
    private final ObjectDeserializer deserializer;
    private final FieldOrderCache fieldOrderCache;
    private final DataFactory dataFactory;
    
    private final Map, PersistenceQueryEncoder> persistenceEncoderByClass = new HashMap, PersistenceQueryEncoder>();
    
    private int clientSideAddGraphDepth = DEFAULT_CLIENT_SIDE_ADD_OBJECT_GRAPH_DEPTH;
    private int clientSideUpdateGraphDepth = DEFAULT_CLIENT_SIDE_UPDATE_OBJECT_GRAPH_DEPTH;
    private int clientSideActionTargetRemotelyGraphDepth = DEFAULT_CLIENT_SIDE_ACTION_TARGET_GRAPH_DEPTH;
    private int clientSideActionParameterGraphDepth = DEFAULT_CLIENT_SIDE_ACTION_PARAMETER_GRAPH_DEPTH;
    
    private int serverSideTouchedObjectGraphDepth = DEFAULT_SERVER_SIDE_TOUCHED_OBJECT_GRAPH_DEPTH;
    private int serverSideRetrievedObjectGraphDepth = DEFAULT_SERVER_SIDE_RETRIEVED_OBJECT_GRAPH_DEPTH;


    /**
     * Factory method.
     */
	public static ObjectEncoderDecoderDefault create(final NakedObjectConfiguration configuration) {
		
		ObjectEncoderDecoderDefault encoderDecoder = new ObjectEncoderDecoderDefault();
		addPersistenceEncoders(configuration, encoderDecoder, EncodingProtocolConstants.ENCODER_CLASS_NAME_LIST);
		addPersistenceEncoders(configuration, encoderDecoder, EncodingProtocolConstants.ENCODER_CLASS_NAME_LIST_DEPRECATED);
		return encoderDecoder;
	}
	
	private static void addPersistenceEncoders(
			final NakedObjectConfiguration configuration,
			final ObjectEncoderDecoderDefault encoder, String encoderClassNameList) {
		String[] encoders = configuration.getList(encoderClassNameList);
	    for (int i = 0; i < encoders.length; i++) {
	        final PersistenceQueryEncoder encoding = InstanceFactory.createInstance(encoders[i], PersistenceQueryEncoder.class);
	        encoder.addPersistenceQueryEncoder(encoding);
	    }
	}

	/**
	 * Package-level visibility (for tests to use only)
	 */
    public ObjectEncoderDecoderDefault(){
    	this.fieldOrderCache = new FieldOrderCache();
    	this.dataFactory = new DataFactoryDefault();
        this.serializer = new ObjectSerializer(dataFactory, fieldOrderCache);
        this.deserializer = new ObjectDeserializer(fieldOrderCache);

        
        addPersistenceQueryEncoder(new PersistenceQueryFindAllInstancesEncoder());
        addPersistenceQueryEncoder(new PersistenceQueryFindByTitleEncoder());
        addPersistenceQueryEncoder(new PersistenceQueryFindByPatternEncoder());
        addPersistenceQueryEncoder(new PersistenceQueryFindUsingApplibQueryDefaultEncoder());
        addPersistenceQueryEncoder(new PersistenceQueryFindUsingApplibQuerySerializableEncoder());
        
        // TODO: look up overrides of depths from Configuration.
    }
    
    public void addPersistenceQueryEncoder(final PersistenceQueryEncoder encoder) {
    	encoder.setObjectEncoder(this);
        persistenceEncoderByClass.put(encoder.getPersistenceQueryClass(), encoder);
    }

    
    ///////////////////////////////////////////////////////////
    // called both client- and server-side only
    ///////////////////////////////////////////////////////////

    /**
     * Creates a ReferenceData that contains the type, version and OID for the specified object. This can only
     * be used for persistent objects.
     * 
     * 

* Called both client and server-side, in multiple locations. */ public final IdentityData encodeIdentityData(final NakedObject object) { Assert.assertNotNull("OID needed for reference", object, object.getOid()); return dataFactory.createIdentityData(object.getSpecification().getFullName(), object.getOid(), object.getVersion()); } /////////////////////////////////////////////////////////// // client-side encoding /////////////////////////////////////////////////////////// /** * Called client-side only: *

    *
  • by {@link ClientSideTransactionManager#endTransaction()} *
*/ public ObjectData encodeMakePersistentGraph(final NakedObject adapter, final KnownObjectsRequest knownObjects) { Assert.assertTrue("transient", adapter.isTransient()); return (ObjectData) encode(adapter, clientSideAddGraphDepth, knownObjects); } /** * Called client-side only: *
    *
  • by {@link ClientSideTransactionManager#endTransaction()} *
*/ public ObjectData encodeGraphForChangedObject(final NakedObject object, final KnownObjectsRequest knownObjects) { return (ObjectData) encode(object, clientSideUpdateGraphDepth, knownObjects); } /** * Called client-side only: *
    *
  • by {@link PropertySetterFacetWrapProxy#setProperty(NakedObject, NakedObject)} *
*/ public EncodableObjectData encodeAsValue(final NakedObject value) { return serializer.serializeEncodeable(value); } /** * Called client-side only: *
    *
  • by {@link ActionInvocationFacetWrapProxy#invoke(NakedObject, NakedObject[])} (calling remotely) *
*/ public ReferenceData encodeActionTarget(final NakedObject target, final KnownObjectsRequest knownObjects) { return serializer.serializeAdapter(target, clientSideActionTargetRemotelyGraphDepth, knownObjects); } /** * Called client-side only: *
    *
  • by {@link ActionInvocationFacetWrapProxy#invoke(NakedObject, NakedObject[])} *
  • by {@link PersistenceQueryFindByPatternEncoder#encode(PersistenceQuery)} *
  • by hibernate's equivalent encoder *
*/ public final Data[] encodeActionParameters( final NakedObjectSpecification[] parameterTypes, final NakedObject[] parameters, final KnownObjectsRequest knownObjects) { final Data parameterData[] = new Data[parameters.length]; for (int i = 0; i < parameters.length; i++) { final NakedObject parameter = parameters[i]; final String type = parameterTypes[i].getFullName(); parameterData[i] = createParameter(type, parameter, knownObjects); } return parameterData; } /** * Called client-side only: *
    *
  • by {@link PersistenceSessionProxy#findInstances(PersistenceQuery)} *
*/ public PersistenceQueryData encodePersistenceQuery(final PersistenceQuery criteria) { final PersistenceQueryEncoder strategy = findPersistenceQueryEncoder(criteria.getClass()); return strategy.encode(criteria); } /////////////////////////////////////////////////////////// // client-side decoding /////////////////////////////////////////////////////////// /** * Called client-side only: *
    *
  • by {@link ActionInvocationFacetWrapProxy#invoke(NakedObject, NakedObject[])} *
*/ public void madePersistent(final NakedObject target, final ObjectData persistedTarget) { deserializer.madePersistent(target, persistedTarget); } /** * Called client-side only, in multiple locations. */ public NakedObject decode(final Data data) { return deserializer.deserialize(data); } public void decode(final ObjectData[] dataArray) { for (int i = 0; i < dataArray.length; i++) { if (LOG.isDebugEnabled()) { LOG.debug("update " + dataArray[i].getOid()); } this.decode(dataArray[i]); } } /////////////////////////////////////////////////////////// // server-side decoding /////////////////////////////////////////////////////////// /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#executeClientAction(ExecuteClientActionRequest)} *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest) *
*/ public NakedObject decode(final Data data, final KnownObjectsRequest knownObjects) { return deserializer.deserialize(data, knownObjects); } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#findInstances(FindInstancesRequest) *
*/ public PersistenceQuery decodePersistenceQuery(final PersistenceQueryData persistenceQueryData) { final Class criteriaClass = persistenceQueryData.getPersistenceQueryClass(); final PersistenceQueryEncoder encoderDecoder = findPersistenceQueryEncoder(criteriaClass); return encoderDecoder.decode(persistenceQueryData); } private PersistenceQueryEncoder findPersistenceQueryEncoder(final Class persistenceQueryClass) { final PersistenceQueryEncoder encoder = persistenceEncoderByClass.get(persistenceQueryClass); if (encoder == null) { throw new NakedObjectsRemoteException("No encoder for " + persistenceQueryClass.getName()); } return encoder; } /////////////////////////////////////////////////////////// // server-side encoding /////////////////////////////////////////////////////////// public AuthorizationResponse encodeAuthorizeResponse(boolean authorized) { return new AuthorizationResponse(authorized); } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#executeClientAction(ExecuteClientActionRequest)} *
*/ public ExecuteClientActionResponse encodeClientActionResult( final ReferenceData[] madePersistent, final Version[] changedVersion, final ObjectData[] updates) { return new ExecuteClientActionResponse(madePersistent, changedVersion, updates); } /** * Encodes a complete set of data for the specified object. * *

* Called server-side only, in several locations: *

    *
  • by {@link ServerFacadeImpl#findInstances(FindInstancesRequest)} *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest)} *
  • by {@link ServerFacadeImpl#resolveImmediately(ResolveObjectRequest)} *
*/ public final ObjectData encodeCompletePersistentGraph(final NakedObject object) { return encode(object, serverSideRetrievedObjectGraphDepth); } /** * Encodes a minimal set of data for the specified object. * *

* Called server-side only: *

    *
  • by {@link ServerFacadeImpl#getObject(GetObjectRequest)} *
  • by {@link ServerFacadeImpl#clearAssociation(ClearAssociationRequest) *
  • by {@link ServerFacadeImpl#clearValue(ClearValueRequest) *
  • by {@link ServerFacadeImpl#executeClientAction(ExecuteClientActionRequest) *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest) *
  • by {@link ServerFacadeImpl#setAssociation(SetAssociationRequest) *
  • by {@link ServerFacadeImpl#setValue(SetValueRequest) *
*/ public ObjectData encodeForUpdate(final NakedObject object) { final ResolveState resolveState = object.getResolveState(); if (resolveState.isSerializing() || resolveState.isGhost()) { throw new NakedObjectsRemoteException("Illegal resolve state: " + object); } return encode(object, serverSideTouchedObjectGraphDepth); } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#resolveField(ResolveFieldRequest)} *
*/ public Data encodeForResolveField(final NakedObject adapter, final String fieldName) { final Oid oid = adapter.getOid(); final NakedObjectSpecification specification = adapter.getSpecification(); final String type = specification.getFullName(); final ResolveState resolveState = adapter.getResolveState(); Data[] fieldContent; final NakedObjectAssociation[] fields = getFieldOrder(specification); fieldContent = new Data[fields.length]; PersistorUtil.start(adapter, adapter.getResolveState().serializeFrom()); final KnownObjectsRequest knownObjects = new KnownObjectsRequest(); for (int i = 0; i < fields.length; i++) { if (fields[i].getId().equals(fieldName)) { final NakedObject field = fields[i].get(adapter); if (field == null) { fieldContent[i] = dataFactory.createNullData(fields[i].getSpecification().getFullName()); } else if (fields[i].getSpecification().isEncodeable()) { fieldContent[i] = serializer.serializeEncodeable(field); } else if (fields[i].isOneToManyAssociation()) { fieldContent[i] = serializer.serializeCollection(field, serverSideRetrievedObjectGraphDepth, knownObjects); } else { NakedObjectsContext.getPersistenceSession().resolveImmediately(field); fieldContent[i] = serializer.serializeAdapter(field, serverSideRetrievedObjectGraphDepth, knownObjects); } break; } } PersistorUtil.end(adapter); // TODO remove the fudge - needed as collections are part of parents, hence parent object gets set as // resolving (is not a ghost) yet it has no version number // return createObjectData(oid, type, fieldContent, resolveState.isResolved(), // !resolveState.isGhost(), object.getVersion()); final ObjectData data = dataFactory.createObjectData(type, oid, resolveState.isResolved(), adapter.getVersion()); data.setFieldContent(fieldContent); return data; // return createObjectData(oid, type, fieldContent, resolveState.isResolved(), object.getVersion()); } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest)} *
*/ public ObjectData encodeMadePersistentGraph(final ObjectData data, final NakedObject object) { final Oid objectsOid = object.getOid(); Assert.assertNotNull(objectsOid); if (objectsOid.hasPrevious()) { final Version version = object.getVersion(); final String type = data.getType(); final ObjectData persistedData = dataFactory.createObjectData(type, objectsOid, true, version); final Data[] allContents = data.getFieldContent(); if (allContents != null) { final int contentLength = allContents.length; final Data persistentContents[] = new Data[contentLength]; final NakedObjectAssociation[] fields = getFieldOrder(object.getSpecification()); for (int i = 0; i < contentLength; i++) { final Data fieldData = allContents[i]; if (fieldData instanceof NullData) { persistentContents[i] = null; } else if (fields[i].isOneToOneAssociation()) { if (fieldData instanceof ObjectData) { final NakedObject fieldReference = fields[i].get(object); persistentContents[i] = encodeMadePersistentGraph((ObjectData) fieldData, fieldReference); } else { persistentContents[i] = null; } } else if (fields[i].isOneToManyAssociation()) { final NakedObject fieldReference = fields[i].get(object); persistentContents[i] = createMadePersistentCollection((CollectionData) fieldData, fieldReference); } } persistedData.setFieldContent(persistentContents); } return persistedData; } else { return null; } } private Data createMadePersistentCollection(final CollectionData collectionData, final NakedObject collection) { final ReferenceData[] elementData = collectionData.getElements(); final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection); final Iterator elements = facet.iterator(collection); for (int i = 0; i < elementData.length; i++) { final NakedObject element = (NakedObject) elements.next(); final Oid oid = element.getOid(); Assert.assertNotNull(oid); elementData[i] = encodeMadePersistentGraph((ObjectData) elementData[i], element); } return collectionData; } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest)} *
*/ public ExecuteServerActionResponse encodeServerActionResult( final NakedObject result, final ObjectData[] updatesData, final ReferenceData[] disposedData, final ObjectData persistedTargetData, final ObjectData[] persistedParametersData, final String[] messages, final String[] warnings) { Data resultData; if (result == null) { resultData = dataFactory.createNullData(""); } else if (result.getSpecification().isCollection()) { resultData = serializer.serializeCollection(result, serverSideRetrievedObjectGraphDepth, new KnownObjectsRequest()); } else if (result.getSpecification().isObject()) { resultData = encodeCompletePersistentGraph(result); } else { throw new UnknownTypeException(result); } return new ExecuteServerActionResponse(resultData, updatesData, disposedData, persistedTargetData, persistedParametersData, messages, warnings); } /** * Called server-side only: *
    *
  • by {@link ServerFacadeImpl#resolveField(ResolveFieldRequest)} *
  • by {@link ServerFacadeImpl#executeServerAction(ExecuteServerActionRequest) *
*/ public NakedObjectAssociation[] getFieldOrder(final NakedObjectSpecification specification) { return fieldOrderCache.getFields(specification); } ///////////////////////////////////////////////////////////////// // Helpers ///////////////////////////////////////////////////////////////// private final Data createParameter(final String type, final NakedObject adapter, final KnownObjectsRequest knownObjects) { if (adapter == null) { return dataFactory.createNullData(type); } if (!adapter.getSpecification().isObject()) { throw new UnknownTypeException(adapter.getSpecification()); } if (adapter.getSpecification().isEncodeable()) { return serializer.serializeEncodeable(adapter); } else { return encode(adapter, clientSideActionParameterGraphDepth, knownObjects); } } private ObjectData encode(final NakedObject adapter, int depth) { return (ObjectData) encode(adapter, depth, new KnownObjectsRequest()); } private ReferenceData encode(final NakedObject adapter, int depth, final KnownObjectsRequest knownObjects) { return serializer.serializeAdapter(adapter, depth, knownObjects); } } // Copyright (c) Naked Objects Group Ltd.




© 2015 - 2024 Weber Informatics LLC | Privacy Policy