Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.remoting;
import static org.jboss.ejb.client.remoting.Protocol.*;
import static org.xnio.IoUtils.safeClose;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import org.jboss.ejb.client.AttachmentKeys;
import org.jboss.ejb.client.EJBClientConfiguration;
import org.jboss.ejb.client.EJBClientInvocationContext;
import org.jboss.ejb.client.EJBLocator;
import org.jboss.ejb.client.EJBReceiver;
import org.jboss.ejb.client.EJBReceiverContext;
import org.jboss.ejb.client.EJBReceiverInvocationContext;
import org.jboss.ejb.client.Logs;
import org.jboss.ejb.client.RequestSendFailedException;
import java.rmi.UnmarshalException;
import org.jboss.ejb.client.StatefulEJBLocator;
import org.jboss.ejb.client.TransactionID;
import org.jboss.ejb.client.annotation.CompressionHint;
import org.jboss.logging.Logger;
import org.jboss.marshalling.ByteOutput;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.CloseHandler;
import org.jboss.remoting3.Connection;
import org.jboss.remoting3.MessageOutputStream;
import org.xnio.IoFuture;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
/**
* A {@link EJBReceiver} which uses JBoss Remoting to communicate with the server for EJB invocations
*
* @author David M. Lloyd
*/
public final class RemotingConnectionEJBReceiver extends EJBReceiver {
public static final String REMOTE = "remote";
public static final String HTTP_REMOTING = "http-remoting";
public static final String HTTPS_REMOTING = "https-remoting";
private static final Logger logger = Logger.getLogger(RemotingConnectionEJBReceiver.class);
private static final String EJB_CHANNEL_NAME = "jboss.ejb";
private final Connection connection;
private final Map channelAssociations = new IdentityHashMap();
/**
* A latch which will be used to wait for the initial module availability report from the server
* after the version handshake between the server and the client is successfully completed.
*/
private final Map moduleAvailabilityReportLatches = new IdentityHashMap();
private final MarshallerFactory marshallerFactory;
private final ReconnectHandler reconnectHandler;
private final OptionMap channelCreationOptions;
private final String remotingProtocol;
private static final Logs log = Logs.MAIN;
/**
* Construct a new instance.
*
* @param connection the connection to associate with
* @param remotingProtocol the remoting protocol in use
*/
public RemotingConnectionEJBReceiver(final Connection connection, final String remotingProtocol) {
this(connection, null, OptionMap.EMPTY, remotingProtocol);
}
/**
* Construct a new instance.
*
* @param connection the connection to associate with
* @deprecated Since 2.0.0. Use {@link #RemotingConnectionEJBReceiver(org.jboss.remoting3.Connection, String)} instead
*/
@Deprecated
public RemotingConnectionEJBReceiver(final Connection connection) {
this(connection, HTTP_REMOTING);
}
/**
* Construct a new instance.
*
* @param connection the connection to associate with
* @param reconnectHandler The {@link org.jboss.ejb.client.remoting.ReconnectHandler} to use when the connection breaks
* @param channelCreationOptions The {@link org.xnio.OptionMap options} to be used during channel creation
* @deprecated Since 2.0.0. Use {@link #RemotingConnectionEJBReceiver(org.jboss.remoting3.Connection, ReconnectHandler, org.xnio.OptionMap, String)} instead
*/
@Deprecated
public RemotingConnectionEJBReceiver(final Connection connection, final ReconnectHandler reconnectHandler, final OptionMap channelCreationOptions) {
this(connection, reconnectHandler, channelCreationOptions, HTTP_REMOTING);
}
/**
* Construct a new instance.
*
* @param connection the connection to associate with
* @param reconnectHandler The {@link org.jboss.ejb.client.remoting.ReconnectHandler} to use when the connection breaks
* @param channelCreationOptions The {@link org.xnio.OptionMap options} to be used during channel creation
* @param remotingProtocol
*/
public RemotingConnectionEJBReceiver(final Connection connection, final ReconnectHandler reconnectHandler, final OptionMap channelCreationOptions, final String remotingProtocol) {
super(connection.getRemoteEndpointName());
this.connection = connection;
this.reconnectHandler = reconnectHandler;
this.remotingProtocol = remotingProtocol;
this.channelCreationOptions = channelCreationOptions == null ? OptionMap.EMPTY : channelCreationOptions;
this.marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");
if (this.marshallerFactory == null) {
throw new RuntimeException("Could not find a marshaller factory for 'river' marshalling strategy");
}
}
@Override
public void associate(final EJBReceiverContext context) {
// a latch for waiting a version handshake
final CountDownLatch versionHandshakeLatch = new CountDownLatch(1);
// setup a latch which will be used for waiting initial module availability report from the server
final CountDownLatch initialModuleAvailabilityLatch = new CountDownLatch(1);
synchronized (this.moduleAvailabilityReportLatches) {
this.moduleAvailabilityReportLatches.put(context, initialModuleAvailabilityLatch);
}
final VersionReceiver versionReceiver = new VersionReceiver(versionHandshakeLatch);
final IoFuture futureChannel = connection.openChannel(EJB_CHANNEL_NAME, this.channelCreationOptions);
futureChannel.addNotifier(new IoFuture.HandlingNotifier() {
public void handleCancelled(final EJBReceiverContext context) {
logger.debugf("Channel open requested cancelled for context %s", context);
context.close();
}
public void handleFailed(final IOException exception, final EJBReceiverContext context) {
logger.error("Failed to open channel for context " + context, exception);
context.close();
}
public void handleDone(final Channel channel, final EJBReceiverContext context) {
channel.addCloseHandler(new CloseHandler() {
public void handleClose(final Channel closed, final IOException exception) {
logger.debugf(exception, "Closing channel%s", closed);
context.close();
}
});
logger.debugf("Channel %s opened for context %s Waiting for version handshake message from server", channel, context);
// receive version message from server
channel.receiveMessage(versionReceiver);
}
}, context);
boolean successfulHandshake = false;
try {
// wait for the handshake to complete
// The time to "wait" for the handshake to complete is the same as the invocation timeout
// set in this EJB client context's configuration, since a handshake is essentially a request
// followed for a wait for the response
final EJBClientConfiguration ejbClientConfiguration = context.getClientContext().getEJBClientConfiguration();
final long versionHandshakeTimeoutInMillis;
if (ejbClientConfiguration == null || ejbClientConfiguration.getInvocationTimeout() <= 0) {
// default to 5000 milli sec
versionHandshakeTimeoutInMillis = 5000;
} else {
versionHandshakeTimeoutInMillis = ejbClientConfiguration.getInvocationTimeout();
}
successfulHandshake = versionHandshakeLatch.await(versionHandshakeTimeoutInMillis, TimeUnit.MILLISECONDS);
if (successfulHandshake) {
final Channel compatibleChannel = versionReceiver.getCompatibleChannel();
final ChannelAssociation channelAssociation = new ChannelAssociation(this, context, compatibleChannel, (byte) versionReceiver.getNegotiatedProtocolVersion(), this.marshallerFactory, this.reconnectHandler, remotingProtocol);
synchronized (this.channelAssociations) {
this.channelAssociations.put(context, channelAssociation);
}
Logs.REMOTING.successfulVersionHandshake(context, compatibleChannel);
} else {
// no version handshake done. close the context
Logs.REMOTING.versionHandshakeNotCompleted(context);
context.close();
// register reconnect handler for retries due to e.g. timeouts
if (this.reconnectHandler != null) {
//only add the reconnect handler if the version handshake did not fail due to an incompatibility
// (latch is not being counted down on failure)
if (!versionReceiver.failedCompatibility()) {
logger.debugf("Adding reconnect handler to client context %s", context.getClientContext());
context.getClientContext().registerReconnectHandler(this.reconnectHandler);
}
}
}
} catch (InterruptedException e) {
context.close();
}
if (successfulHandshake) {
// Now that the version handshake has been completed, let's await the initial module report
// from the server. This initial wait is necessary to ensure that any immediate invocation on the receiver
// doesn't fail due to non-availability of the module report (which effectively means this receiver won't
// know whether it can handle an invocation on a appname/modulename/distinctname combination
try {
final boolean initialReportAvailable = initialModuleAvailabilityLatch.await(5, TimeUnit.SECONDS);
if (!initialReportAvailable) {
// let's log a message and just return back. Don't close the context since it's *not* an error
// that the module report wasn't available in that amount of time.
Logs.REMOTING.initialModuleAvailabilityReportNotReceived(this);
}
} catch (InterruptedException e) {
logger.debugf(e, "Caught InterruptedException while waiting for initial module availability report for %s", this);
}
}
}
@Override
public void disassociate(final EJBReceiverContext context) {
ChannelAssociation channelAssociation = null;
synchronized (this.channelAssociations) {
channelAssociation = this.channelAssociations.remove(context);
}
// close the channel that is associated
if(channelAssociation != null) {
try {
channelAssociation.getChannel().close();
} catch(IOException e) {
logger.warn("Caught IOException when trying to close channel: " + channelAssociation.getChannel(), e);
}
}
}
@Override
public void processInvocation(final EJBClientInvocationContext clientInvocationContext, final EJBReceiverInvocationContext ejbReceiverInvocationContext) throws Exception {
if(System.getSecurityManager() == null) {
processInvocationInternal(clientInvocationContext, ejbReceiverInvocationContext);
} else {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction