
org.nakedobjects.plugins.remoting.shared.ObjectEncoderDeserializer Maven / Gradle / Ivy
package org.nakedobjects.plugins.remoting.shared;
import java.util.Enumeration;
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.commons.exceptions.UnknownTypeException;
import org.nakedobjects.metamodel.facets.collections.modify.CollectionFacet;
import org.nakedobjects.metamodel.facets.object.encodeable.EncodeableFacet;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.Persistability;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.spec.feature.OneToManyAssociation;
import org.nakedobjects.metamodel.spec.feature.OneToOneAssociation;
import org.nakedobjects.metamodel.specloader.SpecificationLoader;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.plugins.remoting.shared.data.CollectionData;
import org.nakedobjects.plugins.remoting.shared.data.Data;
import org.nakedobjects.plugins.remoting.shared.data.EncodeableObjectData;
import org.nakedobjects.plugins.remoting.shared.data.IdentityData;
import org.nakedobjects.plugins.remoting.shared.data.KnownObjects;
import org.nakedobjects.plugins.remoting.shared.data.NullData;
import org.nakedobjects.plugins.remoting.shared.data.ObjectData;
import org.nakedobjects.plugins.remoting.shared.data.ReferenceData;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.persistence.PersistenceSessionHydrator;
import org.nakedobjects.runtime.persistence.PersistorUtil;
import org.nakedobjects.runtime.persistence.adaptermanager.AdapterManager;
import org.nakedobjects.runtime.transaction.updatenotifier.UpdateNotifier;
class ObjectEncoderDeserializer {
private static final Logger LOG = Logger.getLogger(ObjectEncoderDeserializer.class);
private ObjectEncoderDataStructure dataStructure;
/////////////////////////////////////////////////////////
// DataStructure
/////////////////////////////////////////////////////////
public void setDataStructure(final ObjectEncoderDataStructure dataStructure) {
this.dataStructure = dataStructure;
}
/////////////////////////////////////////////////////////
// restore
/////////////////////////////////////////////////////////
public NakedObject restore(final Data data) {
if (data instanceof CollectionData) {
return restoreCollection((CollectionData) data, new KnownObjects());
} else {
return restoreObject(data, new KnownObjects());
}
}
public NakedObject restore(final Data data, final KnownObjects knownObjects) {
if (data instanceof CollectionData) {
return restoreCollection((CollectionData) data, knownObjects);
} else {
return restoreObject(data, knownObjects);
}
}
/////////////////////////////////////////////////////////
// Helper: restoreCollection
/////////////////////////////////////////////////////////
private NakedObject restoreCollection(final CollectionData data, final KnownObjects knownObjects) {
final String collectionType = data.getType();
final NakedObjectSpecification 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
*/
NakedObject collection = getPersistenceSession().createInstance(collectionSpecification);
if (data.getElements() == null) {
LOG.debug("restoring empty collection");
return collection;
} else {
final ReferenceData[] elements = data.getElements();
LOG.debug("restoring collection " + elements.length + " elements");
final NakedObject[] initData = new NakedObject[elements.length];
for (int i = 0; i < elements.length; i++) {
final NakedObject element = restoreObject(elements[i], knownObjects);
LOG.debug("restoring collection element :" + element);
initData[i] = element;
}
final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
facet.init(collection, initData);
return collection;
}
}
/////////////////////////////////////////////////////////
// Helper: restoreObject
/////////////////////////////////////////////////////////
private NakedObject restoreObject(final Data data, final KnownObjects knownObjects) {
if (data instanceof NullData) {
return null;
} else if (data instanceof ObjectData) {
return restoreObjectFromObject((ObjectData) data, knownObjects);
} else if (data instanceof IdentityData) {
return restoreObjectFromIdentity((IdentityData) data, knownObjects);
} else if (data instanceof EncodeableObjectData) {
return restoreEncodable((EncodeableObjectData) data);
} else {
throw new UnknownTypeException(data);
}
}
private NakedObject restoreObjectFromIdentity(final IdentityData data, final KnownObjects knownObjects) {
final Oid oid = data.getOid();
NakedObject object;
/*
* 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
*/
object = getAdapterManager().getAdapterFor(oid);
if (object == null) {
final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());
object = getHydrator().recreateAdapter(oid, specification);
}
return object;
}
private NakedObject restoreObjectFromObject(final ObjectData data, final KnownObjects knownObjects) {
if (knownObjects.containsKey(data)) {
return knownObjects.get(data);
}
final Oid oid = data.getOid();
NakedObject object;
/*
* 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
*/
object = getAdapterManager().getAdapterFor(oid);
if (object != null) {
updateLoadedObject(data, object, knownObjects);
} else if (oid.isTransient()) {
object = restoreTransient(data, knownObjects);
} else {
object = restorePersistentObject(data, oid, knownObjects);
}
return object;
}
private NakedObject restoreTransient(final ObjectData data, final KnownObjects knownObjects) {
final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());
NakedObject object;
object = getHydrator().recreateAdapter(data.getOid(), specification);
if (LOG.isDebugEnabled()) {
LOG.debug("restore transient object " + object);
}
knownObjects.put(object, data);
setUpFields(data, object, knownObjects);
return object;
}
private NakedObject restorePersistentObject(final ObjectData data, final Oid oid, final KnownObjects knownObjects) {
// unknown object; create an instance
final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(data.getType());
NakedObject object;
object = getHydrator().recreateAdapter(oid, specification);
if (data.getFieldContent() != null) {
object.setOptimisticLock(data.getVersion());
ResolveState state;
state = data.hasCompleteData() ? ResolveState.RESOLVING : ResolveState.RESOLVING_PART;
LOG.debug("restoring existing object (" + state.name() + ") " + object);
setupFields(data, object, state, knownObjects);
}
return object;
}
private NakedObject restoreEncodable(final EncodeableObjectData encodeableObjectData) {
NakedObject value;
if (encodeableObjectData.getEncodedObjectData() == null) {
value = null;
} else {
final NakedObjectSpecification spec = getSpecificationLoader().loadSpecification(
encodeableObjectData.getType());
final EncodeableFacet encoder = spec.getFacet(EncodeableFacet.class);
value = encoder.fromEncodedString(encodeableObjectData.getEncodedObjectData());
}
return value;
}
/////////////////////////////////////////////////////////
// Helpers: updateLoadedObject
/////////////////////////////////////////////////////////
private void updateLoadedObject(final ObjectData data, final NakedObject object, final KnownObjects knownObjects) {
// object known and we have all the latest data; update/resolve the object
if (data.getFieldContent() != null) {
object.setOptimisticLock(data.getVersion());
final ResolveState state = nextState(object.getResolveState(), data.hasCompleteData());
if (state != null) {
LOG.debug("updating existing object (" + state.name() + ") " + object);
setupFields(data, object, state, knownObjects);
getUpdateNotifier().addChangedObject(object);
}
} else {
if (data.getVersion() != null && data.getVersion().different(object.getVersion())) {
// TODO reload the object
}
}
}
/////////////////////////////////////////////////////////
// Helpers: setupFields
/////////////////////////////////////////////////////////
private void setUpCollectionField(
final ObjectData parentData,
final NakedObject object,
final NakedObjectAssociation field,
final CollectionData content,
final KnownObjects knownObjects) {
if (!content.hasAllElements()) {
final NakedObject collection = field.get(object);
if (collection.getResolveState() != ResolveState.GHOST) {
LOG.debug("No data for collection: " + field.getId());
if (object.getVersion().different(parentData.getVersion())) {
LOG.debug("clearing collection as versions differ: " + object.getVersion() + " " + parentData.getVersion());
final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collection);
facet.init(collection, new NakedObject[0]);
collection.changeState(ResolveState.GHOST);
}
}
return;
} else {
final int size = content.getElements().length;
final NakedObject[] elements = new NakedObject[size];
for (int j = 0; j < elements.length; j++) {
elements[j] = restoreObject(content.getElements()[j], knownObjects);
LOG.debug("adding element to " + field.getId() + ": " + elements[j]);
}
final NakedObject col = field.get(object);
final ResolveState initialState = col.getResolveState();
final ResolveState state = nextState(initialState, content.hasAllElements());
if (state != null) {
PersistorUtil.start(col, state);
final NakedObject collection = ((OneToManyAssociation) field).get(object);
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 ObjectData data,
final NakedObject object,
final ResolveState state,
final KnownObjects knownObjects) {
if (object.getResolveState().isDeserializable(state)) {
PersistorUtil.start(object, state);
setUpFields(data, object, knownObjects);
PersistorUtil.end(object);
}
}
private void setUpFields(final ObjectData data, final NakedObject object, final KnownObjects knownObjects) {
final Data[] fieldContent = data.getFieldContent();
if (fieldContent != null && fieldContent.length > 0) {
final NakedObjectAssociation[] fields = dataStructure.getFields(object.getSpecification());
if (fields.length != fieldContent.length) {
throw new NakedObjectsRemoteException("Data received for different number of fields; expected " + fields.length
+ ", but was " + fieldContent.length);
}
for (int i = 0; i < fields.length; i++) {
final NakedObjectAssociation field = fields[i];
final Data fieldData = fieldContent[i];
if (fieldData == null || !field.isPersisted()) {
LOG.debug("no data for field " + field.getId());
continue;
}
if (field.isOneToManyAssociation()) {
setUpCollectionField(data, object, field, (CollectionData) fieldData, knownObjects);
} else if (field.getSpecification().isEncodeable()) {
setUpEncodedField(object, (OneToOneAssociation) field, fieldData);
} else {
setUpReferenceField(object, (OneToOneAssociation) field, fieldData, knownObjects);
}
}
}
}
private void setUpReferenceField(
final NakedObject object,
final OneToOneAssociation field,
final Data data,
final KnownObjects knownObjects) {
NakedObject associate;
associate = restoreObject(data, knownObjects);
LOG.debug("setting association for field " + field.getId() + ": " + associate);
field.initAssociation(object, associate);
}
private void setUpEncodedField(final NakedObject object, final OneToOneAssociation field, final Data data) {
String value;
if (data instanceof NullData) {
field.initAssociation(object, null);
} else {
value = ((EncodeableObjectData) data).getEncodedObjectData();
final EncodeableFacet encoder = field.getSpecification().getFacet(EncodeableFacet.class);
final NakedObject valueAdapter = encoder.fromEncodedString(value);
LOG.debug("setting value for field " + field.getId() + ": " + valueAdapter);
field.initAssociation(object, valueAdapter);
}
}
/////////////////////////////////////////////////////////
// madePersistent
/////////////////////////////////////////////////////////
public void madePersistent(final NakedObject objectAdapter, final ObjectData update) {
if (update == null) {
return;
}
if (objectAdapter.isTransient() &&
objectAdapter.getSpecification().persistability() != Persistability.TRANSIENT) {
getAdapterManager().getAdapterFor(update.getOid()); // causes OID to be updated
objectAdapter.setOptimisticLock(update.getVersion());
objectAdapter.changeState(ResolveState.RESOLVED);
}
final Data[] fieldData = update.getFieldContent();
if (fieldData == null) {
return;
}
final NakedObjectAssociation[] fields = dataStructure.getFields(objectAdapter.getSpecification());
for (int i = 0; i < fieldData.length; i++) {
if (fieldData[i] == null) {
continue;
}
if (fields[i].isOneToOneAssociation()) {
final NakedObject field = ((OneToOneAssociation) fields[i]).get(objectAdapter);
final ObjectData fieldContent = (ObjectData) update.getFieldContent()[i];
if (field != null) {
madePersistent(field, fieldContent);
}
} else if (fields[i].isOneToManyAssociation()) {
final CollectionData collectionData = (CollectionData) update.getFieldContent()[i];
final NakedObject collectionAdapter = fields[i].get(objectAdapter);
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 NakedObject element = (NakedObject) 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 NakedObjectsContext.getSpecificationLoader();
}
private UpdateNotifier getUpdateNotifier() {
return NakedObjectsContext.getUpdateNotifier();
}
private PersistenceSession getPersistenceSession() {
return NakedObjectsContext.getPersistenceSession();
}
private AdapterManager getAdapterManager() {
return getPersistenceSession().getAdapterManager();
}
private PersistenceSessionHydrator getHydrator() {
return getPersistenceSession();
}
}
// Copyright (c) Naked Objects Group Ltd.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy