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

org.jboss.ejb.client.EJBInvocationHandler Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.jboss.ejb.client;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.RemoteException;
import java.security.PrivilegedAction;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

import jakarta.ejb.EJBException;
import jakarta.ejb.EJBHome;
import jakarta.ejb.EJBObject;

import org.jboss.ejb._private.Logs;
import org.wildfly.common.Assert;
import org.wildfly.discovery.Discovery;
import org.wildfly.security.auth.client.AuthenticationContext;

import static java.security.AccessController.doPrivileged;

/**
 * @param  the proxy view type
 * @author David M. Lloyd
 */
final class EJBInvocationHandler extends Attachable implements InvocationHandler, Serializable {

    private static final long serialVersionUID = 946555285095057230L;

    private static final Supplier DISCOVERY_SUPPLIER = doPrivileged((PrivilegedAction>) Discovery.getContextManager()::getPrivilegedSupplier);

    private static final int MAX_RETRIES = SecurityUtils.getInteger(SystemProperties.MAX_ENTRIES, 8);

    private final transient boolean async;

    private transient String toString;
    private transient String toStringProxy;

    private final AtomicReference> locatorRef;

    private volatile Affinity weakAffinity = Affinity.NONE;

    // -1 = use global value
    private volatile long invocationTimeout = -1L;

    /**
     * The sticky authentication context supplier for this proxy.
     */
    private volatile Supplier authenticationContextSupplier;

    /**
     * Construct a new instance.
     *
     * @param locator the initial Enterprise Beans locator (not {@code null})
     * @param authenticationContextSupplier the sticky authentication context supplier, or {@code null} to always capture the current context
     */
    EJBInvocationHandler(final EJBLocator locator, final Supplier authenticationContextSupplier) {
        Assert.checkNotNullParam("locator", locator);
        this.authenticationContextSupplier = authenticationContextSupplier;
        this.locatorRef = new AtomicReference<>(locator);
        async = false;
        if (locator instanceof StatefulEJBLocator) {
            // set the weak affinity to the node on which the session was created
            setWeakAffinity(locator.getAffinity());

            if (Logs.INVOCATION.isDebugEnabled()) {
                Logs.INVOCATION.debugf("EJBInvocationHandler: setting weak affinity = %s", locator.getAffinity());
            }
        }
    }

    /**
     * Construct a new asynchronous instance.
     *
     * @param other the synchronous invocation handler
     */
    EJBInvocationHandler(final EJBInvocationHandler other) {
        super(other);
        final EJBLocator locator = other.locatorRef.get();
        locatorRef = new AtomicReference<>(locator);
        authenticationContextSupplier = other.authenticationContextSupplier;
        async = true;
        if (locator instanceof StatefulEJBLocator) {
            // set the weak affinity to the node on which the session was created
            setWeakAffinity(locator.getAffinity());

            if (Logs.INVOCATION.isDebugEnabled()) {
                Logs.INVOCATION.debugf("EJBInvocationHandler(constructor): setting weak affinity = %s", locator.getAffinity());
            }
        }
    }

    public Object invoke(final Object rawProxy, final Method method, final Object... args) throws Exception {
        final T proxy = locatorRef.get().getViewType().cast(rawProxy);
        final EJBProxyInformation.ProxyMethodInfo methodInfo = locatorRef.get().getProxyInformation().getProxyMethodInfo(method);
        return invoke(proxy, methodInfo, args);
    }

    EJBProxyInformation.ProxyMethodInfo getProxyMethodInfo(EJBMethodLocator methodLocator) {
        return locatorRef.get().getProxyInformation().getProxyMethodInfo(methodLocator);
    }

    Object invoke(final Object proxy, final EJBProxyInformation.ProxyMethodInfo methodInfo, final Object... args) throws Exception {
        final Method method = methodInfo.getMethod();
        switch (methodInfo.getMethodType()) {
            case EJBProxyInformation.MT_EQUALS:
            case EJBProxyInformation.MT_IS_IDENTICAL: {
                assert args.length == 1; // checked by EJBProxyInformation
                if (args[0] instanceof Proxy) {
                    final InvocationHandler handler = Proxy.getInvocationHandler(args[0]);
                    if (handler instanceof EJBInvocationHandler) {
                        return Boolean.valueOf(equals(handler));
                    }
                }
                return Boolean.FALSE;
            }
            case EJBProxyInformation.MT_HASH_CODE: {
                // TODO: cache instance?
                return Integer.valueOf(locatorRef.get().hashCode());
            }
            case EJBProxyInformation.MT_TO_STRING: {
                final String s = toStringProxy;
                return s != null ? s : (toStringProxy = String.format("Proxy for remote EJB %s", locatorRef.get()));
            }
            case EJBProxyInformation.MT_GET_PRIMARY_KEY: {
                if (locatorRef.get().isEntity()) {
                    return locatorRef.get().narrowAsEntity(EJBObject.class).getPrimaryKey();
                }
                throw new RemoteException("Cannot invoke getPrimaryKey() on " + proxy);
            }
            case EJBProxyInformation.MT_GET_HANDLE: {
                // TODO: cache instance
                return EJBHandle.create(locatorRef.get().narrowTo(EJBObject.class));
            }
            case EJBProxyInformation.MT_GET_HOME_HANDLE: {
                if (locatorRef.get() instanceof EJBHomeLocator) {
                    // TODO: cache instance
                    return EJBHomeHandle.create(locatorRef.get().narrowAsHome(EJBHome.class));
                }
                throw new RemoteException("Cannot invoke getHomeHandle() on " + proxy);
            }
        }
        // otherwise it's a business method
        assert methodInfo.getMethodType() == EJBProxyInformation.MT_BUSINESS;
        final EJBClientContext clientContext = EJBClientContext.getCurrent();
        final Discovery discoveryContext = DISCOVERY_SUPPLIER.get();

        if (Logs.INVOCATION.isDebugEnabled()) {
            Logs.INVOCATION.debugf("Calling invoke(module = %s, strong affinity = %s, weak affinity = %s): ", locatorRef.get().getIdentifier(), locatorRef.get().getAffinity(), weakAffinity);
        }
        final EJBClientInvocationContext invocationContext = new EJBClientInvocationContext(this, clientContext, proxy, args, methodInfo, MAX_RETRIES, authenticationContextSupplier, discoveryContext);
        invocationContext.setLocator(locatorRef.get());
        invocationContext.setBlockingCaller(true);
        invocationContext.setWeakAffinity(getWeakAffinity());

        try {
            // send the request
            invocationContext.sendRequestInitial();

            if (! async && ! methodInfo.isClientAsync()) {
                // wait for invocation to complete
                return invocationContext.awaitResponse();
            }
            // proceed asynchronously
            invocationContext.setBlockingCaller(false);
            // force async...
            if (method.getReturnType() == Future.class) {
                return invocationContext.getFutureResponse();
            } else if (method.getReturnType() == void.class) {
                invocationContext.setDiscardResult();
                // Void return
                return null;
            } else {
                // wrap return always
                EJBClient.setFutureResult(invocationContext.getFutureResponse());
                return null;
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            boolean remoteException = false;
            for (Class exception : method.getExceptionTypes()) {
                if (exception.isAssignableFrom(e.getClass())) {
                    throw e;
                } else if (RemoteException.class.equals(exception)) {
                    remoteException = true;
                }
            }
            if (remoteException) {
                throw new RemoteException("Error", e);
            }
            throw new EJBException(e);
        }
    }

    void setWeakAffinity(Affinity newWeakAffinity) {
        weakAffinity = newWeakAffinity;
    }

    Affinity getWeakAffinity() {
        return weakAffinity;
    }

    @SuppressWarnings("unchecked")
    static  EJBInvocationHandler forProxy(T proxy) {
        InvocationHandler handler = Proxy.getInvocationHandler(proxy);
        if (handler instanceof EJBInvocationHandler) {
            return (EJBInvocationHandler) handler;
        }
        throw Logs.MAIN.proxyNotOurs(proxy, EJBClient.class.getName());
    }

    @SuppressWarnings("unused")
    protected Object writeReplace() {
        return new SerializedEJBInvocationHandler(locatorRef.get(), async);
    }

    EJBInvocationHandler getAsyncHandler() {
        return async ? this : new EJBInvocationHandler(this);
    }

    boolean isAsyncHandler() {
        return this.async;
    }

    EJBLocator getLocator() {
        return locatorRef.get();
    }

    /**
     * Determine whether this object is equal to another.
     *
     * @param other the other object
     * @return {@code true} if they are equal, {@code false} otherwise
     */
    public boolean equals(Object other) {
        return other instanceof EJBInvocationHandler && equals((EJBInvocationHandler)other);
    }

    /**
     * Determine whether this object is equal to another.
     *
     * @param other the other object
     * @return {@code true} if they are equal, {@code false} otherwise
     */
    public boolean equals(InvocationHandler other) {
        return other instanceof EJBInvocationHandler && equals((EJBInvocationHandler)other);
    }

    /**
     * Determine whether this object is equal to another.
     *
     * @param other the other object
     * @return {@code true} if they are equal, {@code false} otherwise
     */
    public boolean equals(EJBInvocationHandler other) {
        return this == other || other != null && locatorRef.get().equals(other.locatorRef.get()) && async == other.async;
    }

    /**
     * Get the hash code of this handler.
     *
     * @return the hash code of this handler
     */
    public int hashCode() {
        int hc = locatorRef.get().hashCode();
        if (async) hc ++;
        return hc;
    }

    public String toString() {
        final String s = toString;
        return s != null ? s : (toString = String.format("Proxy invocation handler for %s", locatorRef.get()));
    }

    void setStrongAffinity(final Affinity newAffinity) {
        final AtomicReference> locatorRef = this.locatorRef;
        EJBLocator oldVal, newVal;
        do {
            oldVal = locatorRef.get();
            if (oldVal.getAffinity().equals(newAffinity)) {
                return;
            }
            newVal = oldVal.withNewAffinity(newAffinity);
        } while (! locatorRef.compareAndSet(oldVal, newVal));
    }

    void setSessionID(final SessionID sessionID) {
        final AtomicReference> locatorRef = this.locatorRef;
        EJBLocator oldVal, newVal;
        do {
            oldVal = locatorRef.get();
            if (oldVal.isStateful()) {
                if (oldVal.asStateful().getSessionId().equals(sessionID)) {
                    // harmless/idempotent
                    return;
                }
                throw Logs.MAIN.ejbIsAlreadyStateful();
            }
            newVal = oldVal.withSession(sessionID);
        } while (! locatorRef.compareAndSet(oldVal, newVal));
    }

    long getInvocationTimeout() {
        return invocationTimeout;
    }

    void setInvocationTimeout(final long invocationTimeout) {
        this.invocationTimeout = invocationTimeout;
    }

    boolean compareAndSetStrongAffinity(final Affinity expectedAffinity, final Affinity newAffinity) {
        Assert.checkNotNullParam("expectedAffinity", expectedAffinity);
        Assert.checkNotNullParam("newAffinity", newAffinity);
        final AtomicReference> locatorRef = this.locatorRef;
        EJBLocator oldVal = locatorRef.get();
        if (! oldVal.getAffinity().equals(expectedAffinity)) {
            return false;
        }
        EJBLocator newVal = oldVal.withNewAffinity(newAffinity);
        return locatorRef.compareAndSet(oldVal, newVal);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy