
org.nakedobjects.plugins.remoting.server.ServerFacadeImpl Maven / Gradle / Ivy
package org.nakedobjects.plugins.remoting.server;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.nakedobjects.applib.Identifier;
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.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.ensure.Assert;
import org.nakedobjects.metamodel.commons.exceptions.NakedObjectException;
import org.nakedobjects.metamodel.commons.exceptions.UnexpectedCallException;
import org.nakedobjects.metamodel.commons.exceptions.UnknownTypeException;
import org.nakedobjects.metamodel.config.ConfigurationConstants;
import org.nakedobjects.metamodel.config.NakedObjectConfiguration;
import org.nakedobjects.metamodel.criteria.InstancesCriteria;
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.feature.NakedObjectAction;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionConstants;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionType;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAssociation;
import org.nakedobjects.metamodel.spec.feature.NakedObjectMember;
import org.nakedobjects.metamodel.spec.feature.OneToManyAssociation;
import org.nakedobjects.metamodel.spec.feature.OneToOneAssociation;
import org.nakedobjects.metamodel.spec.identifier.IdentifierFactory;
import org.nakedobjects.metamodel.specloader.SpecificationLoader;
import org.nakedobjects.metamodel.specloader.internal.NakedObjectActionImpl;
import org.nakedobjects.metamodel.util.CollectionFacetUtils;
import org.nakedobjects.plugins.remoting.shared.NakedObjectsRemoteException;
import org.nakedobjects.plugins.remoting.shared.ObjectEncoder;
import org.nakedobjects.plugins.remoting.shared.ServerFacade;
import org.nakedobjects.plugins.remoting.shared.data.ClientActionResultData;
import org.nakedobjects.plugins.remoting.shared.data.CriteriaData;
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.plugins.remoting.shared.data.ServerActionResultData;
import org.nakedobjects.plugins.remoting.shared.transaction.ClientTransactionEvent;
import org.nakedobjects.runtime.authentication.AuthenticationManager;
import org.nakedobjects.runtime.authentication.PasswordAuthenticationRequest;
import org.nakedobjects.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.PersistenceConstants;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.transaction.NakedObjectTransactionManager;
import org.nakedobjects.runtime.transaction.messagebroker.MessageBroker;
import org.nakedobjects.runtime.transaction.updatenotifier.UpdateNotifier;
/**
* previously called ServerDistribution.
*/
public class ServerFacadeImpl implements ServerFacade {
private static final Logger LOG = Logger.getLogger(ServerFacadeImpl.class);
private final AuthenticationManager authenticationManager;
private ObjectEncoder encoder;
public ServerFacadeImpl(final AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
////////////////////////////////////////////////////////////////
// init, shutdown
////////////////////////////////////////////////////////////////
public void init() {}
public void shutdown() {}
////////////////////////////////////////////////////////////////
// Session
////////////////////////////////////////////////////////////////
public void closeSession(final AuthenticationSession session) {
authenticationManager.closeSession(session);
}
////////////////////////////////////////////////////////////////
// Authentication
////////////////////////////////////////////////////////////////
public AuthenticationSession authenticate(final String userNameAndPassword) {
final PasswordAuthenticationRequest request = extractAuthenticationRequest(userNameAndPassword);
return authenticationManager.authenticate(request);
}
private PasswordAuthenticationRequest extractAuthenticationRequest(final String userNameAndPassword) {
final int delimiter = userNameAndPassword.indexOf("::");
final String username = userNameAndPassword.substring(0, delimiter);
final String password = userNameAndPassword.substring(delimiter + 2);
final PasswordAuthenticationRequest request = new PasswordAuthenticationRequest(username, password);
return request;
}
////////////////////////////////////////////////////////////////
// Authorization
////////////////////////////////////////////////////////////////
public boolean authoriseVisibility(final AuthenticationSession session, final String memberName) {
return getMember(memberName).isVisible(session, null).isAllowed();
}
public boolean authoriseUsability(final AuthenticationSession session, final String memberName) {
return getMember(memberName).isUsable(session, null).isAllowed();
}
private NakedObjectMember getMember(final String memberName) {
final Identifier id = IdentifierFactory.fromIdentityString(memberName);
final NakedObjectSpecification specification = getSpecificationLoader().loadSpecification(id.getClassName());
if (id.isPropertyOrCollection()) {
return getAssociationElseThrowException(id, specification);
} else {
return getActionElseThrowException(id, specification);
}
}
private NakedObjectMember getActionElseThrowException(final Identifier id, final NakedObjectSpecification specification) {
NakedObjectMember member =
specification.getObjectAction(
NakedObjectActionConstants.USER, id.getMemberName(), getMemberParameterSpecifications(id));
if (member == null) {
throw new NakedObjectException("No user action found for id " + id);
}
return member;
}
private NakedObjectMember getAssociationElseThrowException(final Identifier id, final NakedObjectSpecification specification) {
NakedObjectMember member = specification.getAssociation(id.getMemberName());
if (member == null) {
throw new NakedObjectException("No property or collection found for id " + id);
}
return member;
}
private NakedObjectSpecification[] getMemberParameterSpecifications(final Identifier id) {
final String[] parameters = id.getMemberParameterNames();
final NakedObjectSpecification[] specifications = new NakedObjectSpecification[parameters.length];
for (int i = 0; i < parameters.length; i++) {
specifications[i] = getSpecificationLoader().loadSpecification(parameters[i]);
}
return specifications;
}
////////////////////////////////////////////////////////////////
// setAssociation, setValue, clearAssociation, clearValue
////////////////////////////////////////////////////////////////
public ObjectData[] setAssociation(
final AuthenticationSession session,
final String fieldIdentifier,
final IdentityData target,
final IdentityData associated) {
if (LOG.isDebugEnabled()) {
LOG.debug("request setAssociation " + fieldIdentifier + " on " + target + " with " + associated + " for " + session);
}
final NakedObject inObject = getPersistentNakedObject(session, target);
final NakedObject associate = getPersistentNakedObject(session, associated);
final NakedObjectAssociation association = inObject.getSpecification().getAssociation(fieldIdentifier);
if (!association.isVisible(session, inObject).isAllowed() ||
association.isUsable(session, inObject).isVetoed()) {
throw new NakedObjectException("can't modify field as not visible or editable");
}
if (association instanceof OneToOneAssociation) {
((OneToOneAssociation) association).setAssociation(inObject, associate);
} else {
((OneToManyAssociation) association).addElement(inObject, associate);
}
return getUpdates();
}
public ObjectData[] setValue(
final AuthenticationSession session,
final String fieldIdentifier,
final IdentityData target,
final EncodeableObjectData encodeableObjectData) {
Assert.assertNotNull(encodeableObjectData);
if (LOG.isDebugEnabled()) {
LOG.debug("request setValue " + fieldIdentifier + " on " + target + " with " + encodeableObjectData + " for " + session);
}
final NakedObject inObject = getPersistentNakedObject(session, target);
final OneToOneAssociation association = (OneToOneAssociation) inObject.getSpecification().getAssociation(fieldIdentifier);
if (!association.isVisible(session, inObject).isAllowed() || association.isUsable(session, inObject).isVetoed()) {
throw new NakedObjectException("can't modify field as not visible or editable");
}
final String encodedObject = encodeableObjectData.getEncodedObjectData();
final NakedObjectSpecification specification = association.getSpecification();
final NakedObject adapter = restoreLeafObject(encodedObject, specification);
association.setAssociation(inObject, adapter);
return getUpdates();
}
public ObjectData[] clearAssociation(
final AuthenticationSession session,
final String fieldIdentifier,
final IdentityData target,
final IdentityData associated) {
if (LOG.isDebugEnabled()) {
LOG.debug("request clearAssociation " + fieldIdentifier + " on " + target + " of " + associated + " for " + session);
}
final NakedObject inObject = getPersistentNakedObject(session, target);
final NakedObject associate = getPersistentNakedObject(session, associated);
final NakedObjectSpecification specification = inObject.getSpecification();
final NakedObjectAssociation association = specification.getAssociation(fieldIdentifier);
if (!association.isVisible(session, inObject).isAllowed() || association.isUsable(session, inObject).isVetoed()) {
throw new NakedObjectException("can't modify field as not visible or editable");
}
if (association instanceof OneToOneAssociation) {
((OneToOneAssociation) association).clearAssociation(inObject);
} else {
((OneToManyAssociation) association).removeElement(inObject, associate);
}
return getUpdates();
}
public ObjectData[] clearValue(final AuthenticationSession session, final String fieldIdentifier, final IdentityData target) {
LOG.debug("request clearValue " + fieldIdentifier + " on " + target + " for " + session);
final NakedObject inObject = getPersistentNakedObject(session, target);
final OneToOneAssociation association = (OneToOneAssociation) inObject.getSpecification().getAssociation(fieldIdentifier);
if (!association.isVisible(session, inObject).isAllowed() || association.isUsable(session, inObject).isVetoed()) {
throw new NakedObjectException("can't modify field as not visible or editable");
}
association.clearAssociation(inObject);
return getUpdates();
}
private ObjectData[] convertToNakedCollection(final NakedObject instances) {
final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(instances);
final ObjectData[] data = new ObjectData[facet.size(instances)];
final Enumeration elements = facet.elements(instances);
int i = 0;
while (elements.hasMoreElements()) {
final NakedObject element = (NakedObject) elements.nextElement();
data[i++] = encoder.createCompletePersistentGraph(element);
}
return data;
}
////////////////////////////////////////////////////////////////
// executeClientAction
////////////////////////////////////////////////////////////////
public ClientActionResultData executeClientAction(final AuthenticationSession session, final ReferenceData[] data, final int[] types) {
if (LOG.isDebugEnabled()) {
LOG.debug("execute client action for " + session);
LOG.debug("start transaction");
}
getTransactionManager().startTransaction();
try {
final KnownObjects knownObjects = new KnownObjects();
final NakedObject[] persistedObjects = new NakedObject[data.length];
final NakedObject[] disposedObjects = new NakedObject[data.length];
final NakedObject[] changedObjects = new NakedObject[data.length];
for (int i = 0; i < data.length; i++) {
NakedObject object;
switch (types[i]) {
case ClientTransactionEvent.ADD:
object = encoder.restore(data[i], knownObjects);
persistedObjects[i] = object;
if (object.getOid().isTransient()) { // confirm that the graph has not been made
// persistent earlier in this loop
LOG.debug(" makePersistent " + data[i]);
getPersistenceSession().makePersistent(object);
}
break;
case ClientTransactionEvent.CHANGE:
final NakedObject obj = getPersistentNakedObject(data[i]);
obj.checkLock(data[i].getVersion());
object = encoder.restore(data[i], knownObjects);
LOG.debug(" objectChanged " + data[i]);
getPersistenceSession().objectChanged(object);
changedObjects[i] = object;
break;
case ClientTransactionEvent.DELETE:
final NakedObject inObject = getPersistentNakedObject(data[i]);
inObject.checkLock(data[i].getVersion());
LOG.debug(" destroyObject " + data[i] + " for " + session);
disposedObjects[i] = inObject;
getPersistenceSession().destroyObject(inObject);
break;
}
}
LOG.debug(" end transaction");
getTransactionManager().endTransaction();
final ReferenceData[] madePersistent = new ReferenceData[data.length];
final Version[] changedVersion = new Version[data.length];
for (int i = 0; i < data.length; i++) {
switch (types[i]) {
case ClientTransactionEvent.ADD:
madePersistent[i] = encoder.createIdentityData(persistedObjects[i]);
break;
case ClientTransactionEvent.CHANGE:
changedVersion[i] = changedObjects[i].getVersion();
break;
}
}
return encoder.createClientActionResult(madePersistent, changedVersion, getUpdates());
} catch (final RuntimeException e) {
LOG.info("abort transaction", e);
getTransactionManager().abortTransaction();
throw e;
}
}
////////////////////////////////////////////////////////////////
// executeServerAction
////////////////////////////////////////////////////////////////
public ServerActionResultData executeServerAction(
final AuthenticationSession session,
final String actionType,
final String actionIdentifier,
final ReferenceData target,
final Data[] parameterData) {
if (LOG.isDebugEnabled()) {
LOG.debug("request executeAction " + actionIdentifier + " on " + target + " for " + session);
}
NakedObject object;
final KnownObjects knownObjects = new KnownObjects();
if (target instanceof IdentityData) {
object = getPersistentNakedObject(session, (IdentityData) target);
} else if (target instanceof ObjectData) {
object = encoder.restore(target, knownObjects);
} else if (target == null) {
object = null;
} else {
throw new NakedObjectException();
}
final NakedObjectAction action = getActionMethod(actionType, actionIdentifier, parameterData, object);
final NakedObject[] parameters = getParameters(session, parameterData, knownObjects);
if (action == null) {
throw new NakedObjectsRemoteException("Could not find method " + actionIdentifier);
}
final NakedObject result = action.execute(object, parameters);
ObjectData persistedTarget;
if (target == null) {
persistedTarget = null;
} else if (target instanceof ObjectData) {
persistedTarget = encoder.createMadePersistentGraph((ObjectData) target, object);
} else {
persistedTarget = null;
}
final ObjectData[] persistedParameters = new ObjectData[parameterData.length];
for (int i = 0; i < persistedParameters.length; i++) {
if (action.getParameters()[i].getSpecification().isObject() && parameterData[i] instanceof ObjectData) {
persistedParameters[i] = encoder.createMadePersistentGraph((ObjectData) parameterData[i], parameters[i]);
}
}
final List messages = getMessageBroker().getMessages();
final List warnings = getMessageBroker().getWarnings();
// TODO for efficiency, need to remove the objects in the results graph from the updates set
return encoder.createServerActionResult(result, getUpdates(), getDisposed(), persistedTarget, persistedParameters,
messages.toArray(new String[0]), warnings.toArray(new String[0]));
}
private MessageBroker getMessageBroker() {
return NakedObjectsContext.getMessageBroker();
}
private NakedObjectAction getActionMethod(
final String actionType,
final String actionIdentifier,
final Data[] parameterData,
final NakedObject object) {
final NakedObjectSpecification[] parameterSpecifiactions = new NakedObjectSpecification[parameterData.length];
for (int i = 0; i < parameterSpecifiactions.length; i++) {
parameterSpecifiactions[i] = getSpecification(parameterData[i].getType());
}
final NakedObjectActionType type = NakedObjectActionImpl.getType(actionType);
final int pos = actionIdentifier.indexOf('#');
final String className = actionIdentifier.substring(0, pos);
final String methodName = actionIdentifier.substring(pos + 1);
if (object == null) {
throw new UnexpectedCallException("object not specified");
}
return object.getSpecification().getObjectAction(type, methodName, parameterSpecifiactions);
}
private NakedObject[] getParameters(final AuthenticationSession session, final Data[] parameterData, final KnownObjects knownObjects) {
final NakedObject[] parameters = new NakedObject[parameterData.length];
for (int i = 0; i < parameters.length; i++) {
final Data data = parameterData[i];
if (data instanceof NullData) {
continue;
}
if (data instanceof IdentityData) {
parameters[i] = getPersistentNakedObject(session, (IdentityData) data);
} else if (data instanceof ObjectData) {
parameters[i] = encoder.restore(data, knownObjects);
} else if (data instanceof EncodeableObjectData) {
final NakedObjectSpecification valueSpecification = getSpecificationLoader().loadSpecification(
data.getType());
final String valueData = ((EncodeableObjectData) data).getEncodedObjectData();
final NakedObject value = restoreLeafObject(valueData, valueSpecification);
/*
* NakedValue value =
* NakedObjectsContext.getObjectLoader().createValueInstance(valueSpecification);
* value.restoreFromEncodedString(valueData);
*/
parameters[i] = value;
} else {
throw new UnknownTypeException(data);
}
}
return parameters;
}
private ReferenceData[] getDisposed() {
final List list = new ArrayList();
for(NakedObject element: getUpdateNotifier().getDisposedObjects()) {
list.add(encoder.createIdentityData(element));
}
return (ReferenceData[]) list.toArray(new ReferenceData[list.size()]);
}
////////////////////////////////////////////////////////////////
// getObject, resolve
////////////////////////////////////////////////////////////////
public ObjectData getObject(final AuthenticationSession session, final Oid oid, final String specificationName) {
final NakedObjectSpecification specification = getSpecification(specificationName);
final NakedObject object = getPersistenceSession().loadObject(oid, specification);
return encoder.createForUpdate(object);
}
public Data resolveField(final AuthenticationSession session, final IdentityData target, final String fieldName) {
if (LOG.isDebugEnabled()) {
LOG.debug("request resolveField " + target + "/" + fieldName + " for " + session);
}
final NakedObjectSpecification spec = getSpecification(target.getType());
final NakedObjectAssociation field = spec.getAssociation(fieldName);
// NakedObject object = NakedObjects.getObjectManager().getObject(target.getOid(), spec);
final NakedObject object = NakedObjectsContext.getPersistenceSession().recreateAdapter(target.getOid(), spec);
getPersistenceSession().resolveField(object, field);
return encoder.createForResolveField(object, fieldName);
}
public ObjectData resolveImmediately(final AuthenticationSession session, final IdentityData target) {
if (LOG.isDebugEnabled()) {
LOG.debug("request resolveImmediately " + target + " for " + session);
}
final NakedObjectSpecification spec = getSpecification(target.getType());
final NakedObject object = getPersistenceSession().loadObject(target.getOid(), spec);
if (object.getResolveState().canChangeTo(ResolveState.RESOLVING)) {
// this is need when the object store does not load the object fully in the getObject() above
getPersistenceSession().resolveImmediately(object);
}
return encoder.createCompletePersistentGraph(object);
}
////////////////////////////////////////////////////////////////
// findInstances, hasInstances
////////////////////////////////////////////////////////////////
public ObjectData[] findInstances(final AuthenticationSession session, final CriteriaData criteriaData) {
final InstancesCriteria criteria = encoder.restoreCriteria(criteriaData);
LOG.debug("request findInstances " + criteria + " for " + session);
final NakedObject instances = getPersistenceSession().findInstances(criteria);
return convertToNakedCollection(instances);
}
public boolean hasInstances(final AuthenticationSession session, final String objectType) {
LOG.debug("request hasInstances of " + objectType + " for " + session);
return getPersistenceSession().hasInstances(getSpecification(objectType));
}
////////////////////////////////////////////////////////////////
// oidForService
////////////////////////////////////////////////////////////////
public IdentityData oidForService(final AuthenticationSession session, final String id) {
final NakedObject service = getPersistenceSession().getService(id);
if (service == null) {
throw new NakedObjectsRemoteException("Failed to find service " + id);
} else {
return encoder.createIdentityData(service);
}
}
////////////////////////////////////////////////////////////////
// getProperties
////////////////////////////////////////////////////////////////
public Properties getProperties() {
final Properties properties = new Properties();
properties.put("test-client", "true");
// pass over services
final NakedObjectConfiguration configuration = NakedObjectsContext.getConfiguration();
final NakedObjectConfiguration serviceProperties = configuration.getProperties(ConfigurationConstants.ROOT + "services");
final Enumeration e = serviceProperties.propertyNames();
while (e.hasMoreElements()) {
final String name = (String) e.nextElement();
properties.put(name, serviceProperties.getString(name));
}
// pass over OID generator
final String oidGeneratorClass = getPersistenceSession().getOidGenerator().getClass().getName();
if (oidGeneratorClass != null) {
properties.put(PersistenceConstants.OID_GENERATOR_CLASS_NAME, oidGeneratorClass);
}
// TODO load up client properties
return properties;
}
////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////
private NakedObjectSpecification getSpecification(final String fullName) {
return getSpecificationLoader().loadSpecification(fullName);
}
private NakedObject getPersistentNakedObject(final AuthenticationSession session, final IdentityData object) {
final NakedObject obj = getPersistentNakedObject(object);
if (LOG.isDebugEnabled()) {
LOG.debug("get object " + object + " for " + session + " --> " + obj);
}
obj.checkLock(object.getVersion());
return obj;
}
private NakedObject getPersistentNakedObject(final ReferenceData object) {
final NakedObjectSpecification spec = getSpecification(object.getType());
final NakedObject obj = getPersistenceSession().loadObject(object.getOid(), spec);
Assert.assertNotNull(obj);
return obj;
}
private NakedObject restoreLeafObject(final String encodedObject, final NakedObjectSpecification specification) {
final EncodeableFacet encoder = specification.getFacet(EncodeableFacet.class);
if (encoder == null) {
throw new NakedObjectException("No encoder for " + specification.getFullName());
}
final NakedObject object = encoder.fromEncodedString(encodedObject);
return object;
}
private ObjectData[] getUpdates() {
final List list = new ArrayList();
for(NakedObject element: getUpdateNotifier().getChangedObjects()) {
list.add(encoder.createForUpdate(element));
}
return (ObjectData[]) list.toArray(new ObjectData[list.size()]);
}
////////////////////////////////////////////////////////////////
// Dependencies (injected)
////////////////////////////////////////////////////////////////
public void setEncoder(final ObjectEncoder objectEncoder) {
this.encoder = objectEncoder;
}
////////////////////////////////////////////////////////////////
// Dependencies (from context)
////////////////////////////////////////////////////////////////
private static SpecificationLoader getSpecificationLoader() {
return NakedObjectsContext.getSpecificationLoader();
}
private static PersistenceSession getPersistenceSession() {
return NakedObjectsContext.getPersistenceSession();
}
private static NakedObjectTransactionManager getTransactionManager() {
return getPersistenceSession().getTransactionManager();
}
private static UpdateNotifier getUpdateNotifier() {
return NakedObjectsContext.getUpdateNotifier();
}
}
// Copyright (c) Naked Objects Group Ltd.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy