net.timewalker.ffmq4.remote.connection.RemoteConnection Maven / Gradle / Ivy
/*
* This file is part of FFMQ.
*
* FFMQ 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 of the License, or
* (at your option) any later version.
*
* FFMQ 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 FFMQ; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.timewalker.ffmq4.remote.connection;
import java.net.URI;
import javax.jms.JMSException;
import javax.jms.Session;
import net.timewalker.ffmq4.FFMQConstants;
import net.timewalker.ffmq4.FFMQException;
import net.timewalker.ffmq4.client.ClientEnvironment;
import net.timewalker.ffmq4.common.connection.AbstractConnection;
import net.timewalker.ffmq4.common.message.AbstractMessage;
import net.timewalker.ffmq4.common.session.AbstractSession;
import net.timewalker.ffmq4.remote.session.RemoteMessageConsumer;
import net.timewalker.ffmq4.remote.session.RemoteSession;
import net.timewalker.ffmq4.transport.PacketTransport;
import net.timewalker.ffmq4.transport.PacketTransportEndpoint;
import net.timewalker.ffmq4.transport.PacketTransportException;
import net.timewalker.ffmq4.transport.PacketTransportFactory;
import net.timewalker.ffmq4.transport.PacketTransportHub;
import net.timewalker.ffmq4.transport.PacketTransportListener;
import net.timewalker.ffmq4.transport.packet.AbstractPacket;
import net.timewalker.ffmq4.transport.packet.AbstractResponsePacket;
import net.timewalker.ffmq4.transport.packet.NotificationPacket;
import net.timewalker.ffmq4.transport.packet.PacketType;
import net.timewalker.ffmq4.transport.packet.query.DeleteTemporaryQueueQuery;
import net.timewalker.ffmq4.transport.packet.query.DeleteTemporaryTopicQuery;
import net.timewalker.ffmq4.transport.packet.query.OpenConnectionQuery;
import net.timewalker.ffmq4.transport.packet.query.RollbackMessageQuery;
import net.timewalker.ffmq4.transport.packet.query.SetClientIDQuery;
import net.timewalker.ffmq4.transport.packet.query.StartConnectionQuery;
import net.timewalker.ffmq4.transport.packet.query.StopConnectionQuery;
import net.timewalker.ffmq4.transport.packet.response.OpenConnectionResponse;
import net.timewalker.ffmq4.utils.ErrorTools;
import net.timewalker.ffmq4.utils.async.AsyncTask;
import net.timewalker.ffmq4.utils.id.IntegerID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* RemoteConnection
*/
public class RemoteConnection extends AbstractConnection implements PacketTransportListener
{
protected static final Log log = LogFactory.getLog(RemoteConnection.class);
// Attributes
private URI transportURI;
// Runtime
private PacketTransport transport;
protected PacketTransportHub transportHub;
protected PacketTransportEndpoint transportEndpoint;
/**
* Constructor
*/
public RemoteConnection( URI transportURI , String userName, String password , String clientID ) throws JMSException
{
super(clientID);
this.transportURI = transportURI;
try
{
connect(userName,password);
}
catch (JMSException e)
{
close();
throw e;
}
}
private void connect( String userName, String password ) throws JMSException
{
log.debug("#"+id+" Creating transport");
try
{
this.transport = PacketTransportFactory.getInstance().createPacketTransport(id,transportURI,ClientEnvironment.getSettings());
this.transport.setListener(this);
this.transport.start();
}
catch (PacketTransportException e)
{
throw new FFMQException("Could not establish transport to "+transportURI,"TRANSPORT_ERROR",e);
}
this.transportHub = new PacketTransportHub(transport);
this.transportEndpoint = transportHub.createEndpoint();
log.debug("#"+id+" Opening connection context");
OpenConnectionQuery query = new OpenConnectionQuery();
query.setUserName(userName);
query.setPassword(password);
query.setClientID(clientID);
OpenConnectionResponse response = (OpenConnectionResponse)transportEndpoint.blockingRequest(query);
// Check protocol version
if (response.getProtocolVersion() != FFMQConstants.TRANSPORT_PROTOCOL_VERSION)
throw new FFMQException("Transport protocol version mismatch (client is "+FFMQConstants.TRANSPORT_PROTOCOL_VERSION+", server is "+response.getProtocolVersion()+")","PROTOCOL_MISMATCH");
}
/* (non-Javadoc)
* @see net.timewalker.ffmq4.common.connection.AbstractConnection#setClientID(java.lang.String)
*/
@Override
public final void setClientID(String clientID) throws JMSException
{
super.setClientID(clientID);
SetClientIDQuery query = new SetClientIDQuery();
query.setClientID(clientID);
transportEndpoint.blockingRequest(query);
}
/* (non-Javadoc)
* @see net.timewalker.ffmq4.common.connection.AbstractConnection#onConnectionClose()
*/
@Override
protected void onConnectionClose()
{
super.onConnectionClose();
try
{
if (transport != null && !transport.isClosed())
{
log.debug("#"+id+" Closing transport");
this.transport.close();
}
}
catch (Exception e)
{
log.error("Could not close transport",e);
}
}
/*
* (non-Javadoc)
* @see net.timewalker.ffmq4.common.connection.AbstractConnection#onConnectionClosed()
*/
@Override
protected void onConnectionClosed()
{
super.onConnectionClosed();
if (transportHub != null)
transportHub.close();
}
/* (non-Javadoc)
* @see net.timewalker.ffmq4.common.connection.AbstractConnection#deleteTemporaryQueue(java.lang.String)
*/
@Override
public final void deleteTemporaryQueue(String queueName) throws JMSException
{
DeleteTemporaryQueueQuery query = new DeleteTemporaryQueueQuery();
query.setQueueName(queueName);
transportEndpoint.blockingRequest(query);
}
/* (non-Javadoc)
* @see net.timewalker.ffmq4.common.connection.AbstractConnection#deleteTemporaryTopic(java.lang.String)
*/
@Override
public final void deleteTemporaryTopic(String topicName) throws JMSException
{
DeleteTemporaryTopicQuery query = new DeleteTemporaryTopicQuery();
query.setTopicName(topicName);
transportEndpoint.blockingRequest(query);
}
/* (non-Javadoc)
* @see javax.jms.Connection#createSession(boolean, int)
*/
@Override
public final Session createSession(boolean transacted, int acknowledgeMode) throws JMSException
{
if (!transacted && acknowledgeMode == Session.SESSION_TRANSACTED)
throw new FFMQException("Acknowledge mode SESSION_TRANSACTED cannot be used for an non-transacted session","INVALID_ACK_MODE");
externalAccessLock.readLock().lock();
try
{
checkNotClosed();
RemoteSession session = new RemoteSession(idProvider.createID(),
this,
transportHub.createEndpoint(),
transacted,
acknowledgeMode);
registerSession(session);
session.remoteInit();
return session;
}
finally
{
externalAccessLock.readLock().unlock();
}
}
/* (non-Javadoc)
* @see javax.jms.Connection#start()
*/
@Override
public final void start() throws JMSException
{
externalAccessLock.readLock().lock();
try
{
checkNotClosed();
if (started)
return;
started = true;
}
finally
{
externalAccessLock.readLock().unlock();
}
transportEndpoint.blockingRequest(new StartConnectionQuery());
}
/* (non-Javadoc)
* @see javax.jms.Connection#stop()
*/
@Override
public final void stop() throws JMSException
{
externalAccessLock.readLock().lock();
try
{
checkNotClosed();
if (!started)
return;
started = false;
}
finally
{
externalAccessLock.readLock().unlock();
}
transportEndpoint.blockingRequest(new StopConnectionQuery());
waitForDeliverySync();
}
/*
* (non-Javadoc)
* @see net.timewalker.ffmq4.remote.transport.PacketTransportListener#packetReceived(net.timewalker.ffmq4.remote.transport.packet.AbstractPacket)
*/
@Override
public final boolean packetReceived(AbstractPacket packet)
{
if (packet.getType() == PacketType.NOTIFICATION)
{
final NotificationPacket notifPacket = (NotificationPacket)packet;
final AbstractMessage prefetchedMessage = notifPacket.getMessage();
boolean acceptedByConsumer = false;
AbstractSession session = lookupRegisteredSession(notifPacket.getSessionId());
if (session != null)
{
RemoteMessageConsumer consumer = (RemoteMessageConsumer)session.lookupRegisteredConsumer(notifPacket.getConsumerId());
if (consumer != null)
acceptedByConsumer = consumer.addToPrefetchQueue(prefetchedMessage,notifPacket.isDonePrefetching());
else
log.debug("#"+id+" No such consumer : #"+notifPacket.getSessionId()+":"+notifPacket.getConsumerId()); // Consumer was concurrently closed or not yet registered
}
else
log.debug("#"+id+" No such session : #"+notifPacket.getSessionId()); // Session was concurrently closed
// If the consumer was already gone, we need to rollback the prefetched message on the server side
if (!acceptedByConsumer)
scheduleRollback(notifPacket.getSessionId(),
notifPacket.getConsumerId(),
prefetchedMessage);
return true;
}
if (packet.getType() == PacketType.R_PING)
return true;
// Standard response, route to target endpoint
transportHub.routeResponse((AbstractResponsePacket)packet);
return true;
}
private void scheduleRollback( final IntegerID sessionId , final IntegerID consumerId , AbstractMessage message )
{
try
{
final String messageId = message.getJMSMessageID();
// Schedule an async rollback
ClientEnvironment.getAsyncTaskManager().execute(new AsyncTask()
{
/*
* (non-Javadoc)
* @see net.timewalker.ffmq4.utils.async.AsyncTask#isMergeable()
*/
@Override
public boolean isMergeable() { return false; }
/*
* (non-Javadoc)
* @see net.timewalker.ffmq4.utils.async.AsyncTask#execute()
*/
@Override
public void execute()
{
RollbackMessageQuery query = new RollbackMessageQuery();
query.setSessionId(sessionId);
query.setConsumerId(consumerId);
query.setMessageId(messageId);
try
{
transportEndpoint.blockingRequest(query);
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
}
});
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
}
/*
* (non-Javadoc)
* @see net.timewalker.ffmq4.remote.transport.PacketTransportListener#packetSent(net.timewalker.ffmq4.remote.transport.packet.AbstractPacket)
*/
@Override
public final void packetSent(AbstractPacket packet)
{
// Nothing
}
/* (non-Javadoc)
* @see net.timewalker.ffmq4.remote.transport.PacketTransportListener#transportClosed(boolean)
*/
@Override
public final void transportClosed(boolean linkFailed,boolean mayBlock)
{
if (linkFailed)
{
close();
if (mayBlock)
exceptionOccured(new FFMQException("Server connection lost","NETWORK_FAILURE"));
else
{
try
{
ClientEnvironment.getAsyncTaskManager().execute(new AsyncTask() {
/*
* (non-Javadoc)
* @see net.timewalker.ffmq3.utils.async.AsyncTask#isMergeable()
*/
@Override
public boolean isMergeable()
{
return false;
}
/*
* (non-Javadoc)
* @see net.timewalker.ffmq3.utils.async.AsyncTask#execute()
*/
@Override
public void execute()
{
exceptionOccured(new FFMQException("Server connection lost","NETWORK_FAILURE"));
}
});
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy