
org.nakedobjects.plugins.remoting.client.facets.ActionInvocationFacetWrapProxy Maven / Gradle / Ivy
package org.nakedobjects.plugins.remoting.client.facets;
import org.apache.log4j.Logger;
import org.nakedobjects.applib.Identifier;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.oid.Oid;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.exceptions.NakedObjectException;
import org.nakedobjects.metamodel.facets.DecoratingFacet;
import org.nakedobjects.metamodel.facets.actions.executed.ExecutedFacet;
import org.nakedobjects.metamodel.facets.actions.executed.ExecutedFacet.Where;
import org.nakedobjects.metamodel.facets.actions.invoke.ActionInvocationFacet;
import org.nakedobjects.metamodel.facets.actions.invoke.ActionInvocationFacetAbstract;
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.NakedObjectActionParameter;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionType;
import org.nakedobjects.plugins.remoting.shared.ObjectEncoder;
import org.nakedobjects.plugins.remoting.shared.ServerFacade;
import org.nakedobjects.plugins.remoting.shared.data.Data;
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.runtime.context.NakedObjectsContext;
import org.nakedobjects.runtime.persistence.ConcurrencyException;
import org.nakedobjects.runtime.persistence.PersistenceSession;
import org.nakedobjects.runtime.persistence.adaptermanager.AdapterManager;
/**
* A reflection peer for executing actions remotely, instead of on the local machine. Any calls to
* execute
are passed over the network to the server for invocation. There are two cases where
* the request is not passed to the server, ie it is executed locally: 1) where the method is static, ie is on
* the class rather than an instance; 2) if the instance is not persistent; 3) if the method is marked as
* 'local'. If a method is marked as being 'remote' then static methods and methods on transient objects will
* be passed to the server.
*
*
* If any of the objects involved have been changed on the server by another process then a
* ConcurrencyException will be passed back to the client and re-thrown.
*
*/
public final class ActionInvocationFacetWrapProxy extends ActionInvocationFacetAbstract implements
DecoratingFacet {
private final static Logger LOG = Logger.getLogger(ActionInvocationFacetWrapProxy.class);
private final ServerFacade connection;
private final ObjectEncoder encoder;
private final ActionInvocationFacet underlyingFacet;
private final NakedObjectAction peer;
public ActionInvocationFacetWrapProxy(
final ActionInvocationFacet underlyingFacet,
final ServerFacade connection,
final ObjectEncoder encoder,
final NakedObjectAction peer) {
super(underlyingFacet.getFacetHolder());
this.underlyingFacet = underlyingFacet;
this.connection = connection;
this.encoder = encoder;
this.peer = peer;
}
public ActionInvocationFacet getDecoratedFacet() {
return underlyingFacet;
}
public NakedObject invoke(final NakedObject target, final NakedObject[] parameters) {
if (isToBeExecutedRemotely(target)) {
/*
* NOTE - only remotely executing actions on objects not collection - due to collections not
* having OIDs yet
*/
return executeRemotely(target, parameters);
} else {
LOG.debug(debug("execute locally", getIdentifier(), target, parameters));
return underlyingFacet.invoke(target, parameters);
}
}
public NakedObjectSpecification getReturnType() {
return underlyingFacet.getReturnType();
}
public NakedObjectSpecification getOnType() {
return underlyingFacet.getOnType();
}
public Identifier getIdentifier() {
return peer.getIdentifier();
}
private NakedObject executeRemotely(final NakedObject targetAdapter, final NakedObject[] parameters) {
final KnownObjects knownObjects = new KnownObjects();
final Data[] parameterObjectData = parameterValues(parameters, knownObjects);
LOG.debug(debug("execute remotely", getIdentifier(), targetAdapter, parameters));
final ReferenceData targetReference = targetAdapter == null ? null : encoder.createActionTarget(targetAdapter, knownObjects);
ServerActionResultData result;
try {
// peer.getFacet()
final NakedObjectActionType type = NakedObjectActionConstants.USER;
final String name = getIdentifier().getClassName() + "#" + getIdentifier().getMemberName();
result = connection.executeServerAction(
getAuthenticationSession(), type.getName(), name, targetReference, parameterObjectData);
} catch (final ConcurrencyException e) {
final Oid source = e.getSource();
if (source == null) {
throw e;
} else {
final NakedObject failedObject = getAdapterManager().getAdapterFor(source);
getPersistenceSession().reload(failedObject);
LOG.info("concurrency conflict: " + e.getMessage());
throw new ConcurrencyException("Object automatically reloaded: " + failedObject.titleString(), e);
}
} catch (final NakedObjectException e) {
LOG.error("remoting exception", e);
throw e;
}
// must deal with transient-now-persistent objects first
if (targetAdapter.isTransient()) {
encoder.madePersistent(targetAdapter, result.getPersistedTarget());
}
final NakedObjectActionParameter[] parameters2 = peer.getParameters();
for (int i = 0; i < parameters.length; i++) {
if (parameters2[i].getSpecification().isObject()) {
encoder.madePersistent(parameters[i], result.getPersistedParameters()[i]);
}
}
final Data returned = result.getReturn();
NakedObject returnedObject = returned instanceof NullData ? null : encoder.restore(returned);
final ObjectData[] updates = result.getUpdates();
for (int i = 0; i < updates.length; i++) {
LOG.debug("update " + updates[i].getOid());
encoder.restore(updates[i]);
}
final ReferenceData[] disposed = result.getDisposed();
for (int i = 0; i < disposed.length; i++) {
final Oid oid = disposed[i].getOid();
LOG.debug("disposed " + oid);
final NakedObject adapter = getAdapterManager().getAdapterFor(oid);
NakedObjectsContext.getUpdateNotifier().addDisposedObject(adapter);
}
String[] messages = result.getMessages();
for (int i = 0; i < messages.length; i++) {
NakedObjectsContext.getMessageBroker().addMessage(messages[i]);
}
messages = result.getWarnings();
for (int i = 0; i < messages.length; i++) {
NakedObjectsContext.getMessageBroker().addWarning(messages[i]);
}
return returnedObject;
}
private boolean isToBeExecutedRemotely(final NakedObject targetAdapter) {
final ExecutedFacet facet = peer.getFacet(ExecutedFacet.class);
final boolean remoteOverride = facet.value() == Where.REMOTELY; // peer.getTarget() ==
// NakedObjectAction.REMOTE;
final boolean localOverride = facet.value() == Where.LOCALLY; // getTarget() ==
// NakedObjectAction.LOCAL;
if (localOverride) {
return false;
}
if (remoteOverride) {
return true;
}
if (targetAdapter.getSpecification().isService()) {
return true;
}
if (targetAdapter == null) {
// for static methods there is no target
return false;
}
final boolean remoteAsPersistent = targetAdapter.isPersistent();
return remoteAsPersistent;
}
private Data[] parameterValues(final NakedObject[] parameters, final KnownObjects knownObjects) {
final NakedObjectSpecification[] parameterTypes = new NakedObjectSpecification[parameters.length];
final NakedObjectActionParameter[] parameters2 = peer.getParameters();
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypes[i] = parameters[i].getSpecification();
}
return encoder.createParameters(parameterTypes, parameters, knownObjects);
}
private String debug(
final String message,
final Identifier identifier,
final NakedObject target,
final NakedObject[] parameters) {
if (LOG.isDebugEnabled()) {
final StringBuffer str = new StringBuffer();
str.append(message);
str.append(" ");
str.append(identifier);
str.append(" on ");
str.append(target);
for (int i = 0; i < parameters.length; i++) {
if (i > 0) {
str.append(',');
}
str.append(parameters[i]);
}
return str.toString();
} else {
return "";
}
}
///////////////////////////////////////////////////////////
// Dependencies (from context)
///////////////////////////////////////////////////////////
private static PersistenceSession getPersistenceSession() {
return NakedObjectsContext.getPersistenceSession();
}
private static AdapterManager getAdapterManager() {
return getPersistenceSession().getAdapterManager();
}
private static AuthenticationSession getAuthenticationSession() {
return NakedObjectsContext.getAuthenticationSession();
}
}
// Copyright (c) Naked Objects Group Ltd.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy