org.jboss.ejb.client.EJBClientInvocationContext Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging 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 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.client;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static java.lang.Thread.holdsLock;
/**
* An invocation context for EJB invocations from an EJB client
*
* @author David M. Lloyd
* @author Jaikiran Pai
*/
public final class EJBClientInvocationContext extends Attachable {
private static final Logs log = Logs.MAIN;
public static final String PRIVATE_ATTACHMENTS_KEY = "org.jboss.ejb.client.invocation.attachments";
// Contextual stuff
private final EJBInvocationHandler> invocationHandler;
private final EJBClientContext ejbClientContext;
// Invocation data
private final Object invokedProxy;
private final Method invokedMethod;
private final Object[] parameters;
// Invocation state
private final Object lock = new Object();
private EJBReceiverInvocationContext.ResultProducer resultProducer;
private State state = State.WAITING;
private AsyncState asyncState = AsyncState.SYNCHRONOUS;
private Object cachedResult;
private Map contextData;
private EJBReceiverInvocationContext receiverInvocationContext;
// nodes to which invocation shouldn't be forwarded to. This is typically for cases
// where a invocation failed on a specific node, for this invocation context, due to the deployment
// being undeployed after the node was selected for handling this invocation but before the client
// could get a undeploy notification for it
private Set excludedNodes = new HashSet();
// Interceptor state
private final EJBClientInterceptor[] interceptorChain;
private int interceptorChainIndex;
private boolean resultDone;
EJBClientInvocationContext(final EJBInvocationHandler> invocationHandler, final EJBClientContext ejbClientContext, final Object invokedProxy, final Method invokedMethod, final Object[] parameters) {
this.invocationHandler = invocationHandler;
this.ejbClientContext = ejbClientContext;
this.invokedProxy = invokedProxy;
this.invokedMethod = invokedMethod;
this.parameters = parameters;
interceptorChain = (EJBClientInterceptor[]) ejbClientContext.getInterceptorChain();
}
enum AsyncState {
SYNCHRONOUS,
ASYNCHRONOUS,
ONE_WAY;
}
enum State {
WAITING,
CANCEL_REQ,
CANCELLED,
READY,
CONSUMING,
FAILED,
DONE,
DISCARDED;
}
/**
* Get a value attached to the proxy.
*
* @param key the attachment key
* @param the value type
* @return the value, or {@code null} if there is none
*/
public T getProxyAttachment(AttachmentKey key) {
return invocationHandler.getAttachment(key);
}
/**
* Remove a value attached to the proxy.
*
* @param key the attachment key
* @param the value type
* @return the value, or {@code null} if there is none
*/
public T removeProxyAttachment(final AttachmentKey key) {
return invocationHandler.removeAttachment(key);
}
EJBInvocationHandler> getInvocationHandler() {
return invocationHandler;
}
/**
* Get the EJB client context associated with this invocation.
*
* @return the EJB client context
*/
public EJBClientContext getClientContext() {
return ejbClientContext;
}
/**
* Get the context data. This same data will be made available verbatim to
* server-side interceptors via the {@code InvocationContext.getContextData()} method, and thus
* can be used to pass data from the client to the server (as long as all map values are
* {@link Serializable}).
*
* @return the context data
*/
public Map getContextData() {
if (contextData == null) {
return contextData = new LinkedHashMap();
} else {
return contextData;
}
}
/**
* Get the locator for the invocation target.
*
* @return the locator
*/
public EJBLocator> getLocator() {
return invocationHandler.getLocator();
}
/**
* Proceed with sending the request normally.
*
* @throws Exception if the request was not successfully sent
*/
public void sendRequest() throws Exception {
final int idx = this.interceptorChainIndex++;
final EJBClientInterceptor[] chain = interceptorChain;
if (idx > chain.length) {
throw Logs.MAIN.sendRequestCalledDuringWrongPhase();
}
if (chain.length == idx) {
final EJBReceiverInvocationContext context = receiverInvocationContext;
if (context == null) {
throw Logs.MAIN.noReceiverAssociatedWithInvocation();
}
context.getEjbReceiverContext().getReceiver().processInvocation(this, context);
} else {
chain[idx].handleInvocation(this);
}
}
/**
* Retry a request which was previously sent but probably failed with a error response.
* Note that this method will throw an {@link IllegalStateException} if a previous request wasn't completed
* for this invocation context
*
* @throws Exception if the retry request was not successfully sent
*/
void retryRequest() throws Exception {
// if a previous request wasn't yet done, then this isn't really a "retry",
// so we error out
final int idx = this.interceptorChainIndex;
final EJBClientInterceptor[] chain = interceptorChain;
if (idx <= chain.length) {
throw Logs.MAIN.cannotRetryRequest();
}
// reset the interceptor index to the beginning of the chain
this.interceptorChainIndex = 0;
// reset the previously set receiver invocation context, since a possibly new receiver
// will be selected during this retry
this.receiverInvocationContext = null;
// send request
this.sendRequest();
}
/**
* Returns a set of nodes which shouldn't be used for handling the invocation represented by this
* invocation context. If there are no such nodes, then this method returns an empty set.
*
* Typically a node is marked as excluded for a {@link EJBClientInvocationContext invocation context}
* if it failed to handle that specific {@link EJBClientInvocationContext invocation}. This can happen,
* for example, if a node was selected for invocation handling and was later incapable of handling it
* due to the deployment being un-deployed. The invocation in such cases will typically be retried by
* marking that node as excluded and selecting a different node (if any) for the retried invocation.
*
* @return
*/
Set getExcludedNodes() {
return Collections.unmodifiableSet(this.excludedNodes);
}
/**
* Marks the passed nodeName
as excluded so that the invocation represented by
* this {@link EJBClientInvocationContext invocation context} will leave it out while selecting a suitable node for
* handling the invocation
*
* @param nodeName The name of the node to be excluded
*/
void markNodeAsExcluded(final String nodeName) {
if (nodeName == null || nodeName.trim().isEmpty()) {
return;
}
this.excludedNodes.add(nodeName);
}
void setReceiverInvocationContext(EJBReceiverInvocationContext context) {
receiverInvocationContext = context;
}
/**
* Get the invocation result from this request. The result is not actually acquired unless all interceptors
* call this method. Should only be called from {@link EJBClientInterceptor#handleInvocationResult(EJBClientInvocationContext)}.
*
* @return the invocation result
* @throws Exception if the invocation did not succeed
*/
public Object getResult() throws Exception {
final EJBReceiverInvocationContext.ResultProducer resultProducer = this.resultProducer;
if (resultDone || resultProducer == null) {
throw Logs.MAIN.getResultCalledDuringWrongPhase();
}
final int idx = this.interceptorChainIndex++;
final EJBClientInterceptor[] chain = interceptorChain;
if (idx == 0) try {
return chain[idx].handleInvocationResult(this);
} finally {
resultDone = true;
final Affinity weakAffinity = getAttachment(AttachmentKeys.WEAK_AFFINITY);
if (weakAffinity != null) {
invocationHandler.setWeakAffinity(weakAffinity);
}
}
else try {
if (chain.length == idx) {
if(System.getSecurityManager() == null) {
return resultProducer.getResult();
} else {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy