Package org.apache.isis.runtimes.dflt.remoting.common.client.facets

Source Code of org.apache.isis.runtimes.dflt.remoting.common.client.facets.ActionInvocationFacetWrapProxy

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/

package org.apache.isis.runtimes.dflt.remoting.common.client.facets;

import java.util.List;

import org.apache.log4j.Logger;

import org.apache.isis.applib.Identifier;
import org.apache.isis.core.commons.authentication.AuthenticationSession;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.facetapi.DecoratingFacet;
import org.apache.isis.core.metamodel.facets.actions.executed.ExecutedFacet;
import org.apache.isis.core.metamodel.facets.actions.executed.ExecutedFacet.Where;
import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet;
import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacetAbstract;
import org.apache.isis.core.metamodel.spec.ActionType;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
import org.apache.isis.runtimes.dflt.remoting.common.data.Data;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.NullData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.ObjectData;
import org.apache.isis.runtimes.dflt.remoting.common.data.common.ReferenceData;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ExecuteServerActionRequest;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.ExecuteServerActionResponse;
import org.apache.isis.runtimes.dflt.remoting.common.exchange.KnownObjectsRequest;
import org.apache.isis.runtimes.dflt.remoting.common.facade.ServerFacade;
import org.apache.isis.runtimes.dflt.remoting.common.protocol.ObjectEncoderDecoder;
import org.apache.isis.runtimes.dflt.runtime.persistence.ConcurrencyException;
import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager;
import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSession;
import org.apache.isis.runtimes.dflt.runtime.system.transaction.MessageBroker;
import org.apache.isis.runtimes.dflt.runtime.system.transaction.UpdateNotifier;
import org.apache.isis.runtimes.dflt.runtime.transaction.messagebroker.MessageList;
import org.apache.isis.runtimes.dflt.runtime.transaction.messagebroker.WarningList;

/**
* A reflection peer for executing actions remotely, instead of on the local
* machine. Any calls to <code>execute</code> 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.
*
* <p>
* 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.
* </p>
*/
public final class ActionInvocationFacetWrapProxy extends ActionInvocationFacetAbstract implements DecoratingFacet<ActionInvocationFacet> {

    private final static Logger LOG = Logger.getLogger(ActionInvocationFacetWrapProxy.class);
    private final ServerFacade serverFacade;
    private final ObjectEncoderDecoder encoder;
    private final ActionInvocationFacet underlyingFacet;
    private final ObjectAction objectAction;

    public ActionInvocationFacetWrapProxy(final ActionInvocationFacet underlyingFacet, final ServerFacade connection, final ObjectEncoderDecoder encoder, final ObjectAction objectAction) {
        super(underlyingFacet.getFacetHolder());
        this.underlyingFacet = underlyingFacet;
        this.serverFacade = connection;
        this.encoder = encoder;
        this.objectAction = objectAction;
    }

    @Override
    public ActionInvocationFacet getDecoratedFacet() {
        return underlyingFacet;
    }

    @Override
    public ObjectAdapter invoke(final ObjectAdapter target, final ObjectAdapter[] 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);
        }
    }

    @Override
    public ObjectSpecification getReturnType() {
        return underlyingFacet.getReturnType();
    }

    @Override
    public ObjectSpecification getOnType() {
        return underlyingFacet.getOnType();
    }

    public Identifier getIdentifier() {
        return objectAction.getIdentifier();
    }

    private ObjectAdapter executeRemotely(final ObjectAdapter targetAdapter, final ObjectAdapter[] parameterAdapters) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(debug("execute remotely", getIdentifier(), targetAdapter, parameterAdapters));
        }

        final KnownObjectsRequest knownObjects = new KnownObjectsRequest();
        final Data[] parameterObjectData = parameterValues(parameterAdapters, knownObjects);
        final ReferenceData targetReference = targetAdapter == null ? null : encoder.encodeActionTarget(targetAdapter, knownObjects);
        ExecuteServerActionResponse response;
        try {
            final ExecuteServerActionRequest request = new ExecuteServerActionRequest(getAuthenticationSession(), ActionType.USER, objectAction.getIdentifier().toNameParmsIdentityString(), targetReference, parameterObjectData);
            response = serverFacade.executeServerAction(request);

            // must deal with transient-now-persistent objects first
            if (targetAdapter.isTransient()) {
                encoder.madePersistent(targetAdapter, response.getPersistedTarget());
            }

            final List<ObjectActionParameter> parameters2 = objectAction.getParameters();
            for (int i = 0; i < parameterAdapters.length; i++) {
                if (parameters2.get(i).getSpecification().isNotCollection()) {
                    encoder.madePersistent(parameterAdapters[i], response.getPersistedParameters()[i]);
                }
            }

            final Data returned = response.getReturn();
            final ObjectAdapter returnedObject = returned instanceof NullData ? null : encoder.decode(returned);

            final ObjectData[] updates = response.getUpdates();
            for (final ObjectData update : updates) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("update " + update.getOid());
                }
                encoder.decode(update);
            }

            final ReferenceData[] disposed = response.getDisposed();
            for (final ReferenceData element : disposed) {
                final Oid oid = element.getOid();
                if (LOG.isDebugEnabled()) {
                    LOG.debug("disposed " + oid);
                }
                final ObjectAdapter adapter = getAdapterManager().getAdapterFor(oid);
                getUpdateNotifier().addDisposedObject(adapter);
            }

            copyMessages(response);

            copyWarnings(response);

            return returnedObject;

        } catch (final ConcurrencyException e) {
            final Oid source = e.getSource();
            if (source == null) {
                throw e;
            }
            final ObjectAdapter failedObject = getAdapterManager().getAdapterFor(source);
            getPersistenceSession().reload(failedObject);
            if (LOG.isInfoEnabled()) {
                LOG.info("concurrency conflict: " + e.getMessage());
            }
            throw new ConcurrencyException("Object automatically reloaded: " + failedObject.titleString(), e);
        } catch (final IsisException e) {
            LOG.error("remoting exception", e);
            throw e;
        }

    }

    private void copyWarnings(final WarningList response) {
        for (final String warning : response.getWarnings()) {
            getMessageBroker().addWarning(warning);
        }
    }

    private void copyMessages(final MessageList response) {
        for (final String message : response.getMessages()) {
            getMessageBroker().addMessage(message);
        }
    }

    private boolean isToBeExecutedRemotely(final ObjectAdapter targetAdapter) {
        final ExecutedFacet facet = objectAction.getFacet(ExecutedFacet.class);

        final boolean localOverride = (facet.value() == Where.LOCALLY);
        if (localOverride) {
            return false;
        }

        final boolean remoteOverride = (facet.value() == Where.REMOTELY);
        if (remoteOverride) {
            return true;
        }

        if (targetAdapter.getSpecification().isService()) {
            return true;
        }

        return targetAdapter.isPersistent();
    }

    private Data[] parameterValues(final ObjectAdapter[] parameters, final KnownObjectsRequest knownObjects) {
        final ObjectSpecification[] parameterTypes = new ObjectSpecification[parameters.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            parameterTypes[i] = parameters[i].getSpecification();
        }
        return encoder.encodeActionParameters(parameterTypes, parameters, knownObjects);
    }

    private static String debug(final String message, final Identifier identifier, final ObjectAdapter target, final ObjectAdapter[] parameters) {
        if (!LOG.isDebugEnabled()) {
            return "";
        }
        final StringBuilder str = new StringBuilder();
        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();
    }

    // /////////////////////////////////////////////////////////
    // Dependencies (from context)
    // /////////////////////////////////////////////////////////

    protected PersistenceSession getPersistenceSession() {
        return IsisContext.getPersistenceSession();
    }

    protected AdapterManager getAdapterManager() {
        return getPersistenceSession().getAdapterManager();
    }

    protected AuthenticationSession getAuthenticationSession() {
        return IsisContext.getAuthenticationSession();
    }

    protected MessageBroker getMessageBroker() {
        return IsisContext.getMessageBroker();
    }

    protected UpdateNotifier getUpdateNotifier() {
        return IsisContext.getUpdateNotifier();
    }

}
TOP

Related Classes of org.apache.isis.runtimes.dflt.remoting.common.client.facets.ActionInvocationFacetWrapProxy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.