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).
/*
* 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_RETRIES, 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 extends T> forProxy(T proxy) {
InvocationHandler handler = Proxy.getInvocationHandler(proxy);
if (handler instanceof EJBInvocationHandler) {
return (EJBInvocationHandler extends T>) 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);
}
}