
org.jboss.ejb.client.EJBClientInvocationContext Maven / Gradle / Ivy
/*
* 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