Package org.hornetq.core.protocol.core.impl

Source Code of org.hornetq.core.protocol.core.impl.RemotingConnectionImpl

/*
* Copyright 2009 Red Hat, Inc.
* Red Hat licenses this file to you 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.hornetq.core.protocol.core.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;

import org.hornetq.api.core.HornetQBuffer;
import org.hornetq.api.core.HornetQException;
import org.hornetq.api.core.HornetQInterruptedException;
import org.hornetq.api.core.Interceptor;
import org.hornetq.api.core.SimpleString;
import org.hornetq.core.protocol.core.Channel;
import org.hornetq.core.protocol.core.CoreRemotingConnection;
import org.hornetq.core.protocol.core.Packet;
import org.hornetq.core.protocol.core.impl.ChannelImpl.CHANNEL_ID;
import org.hornetq.core.protocol.core.impl.wireformat.DisconnectMessage;
import org.hornetq.core.remoting.CloseListener;
import org.hornetq.core.remoting.FailureListener;
import org.hornetq.core.security.HornetQPrincipal;
import org.hornetq.core.client.HornetQClientLogger;
import org.hornetq.core.client.HornetQClientMessageBundle;
import org.hornetq.spi.core.remoting.Connection;
import org.hornetq.utils.SimpleIDGenerator;

/**
* @author <a href="tim.fox@jboss.com">Tim Fox</a>
* @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a>
*/
public class RemotingConnectionImpl implements CoreRemotingConnection
{
   // Constants
   // ------------------------------------------------------------------------------------

   private static final boolean isTrace = HornetQClientLogger.LOGGER.isTraceEnabled();

   // Static
   // ---------------------------------------------------------------------------------------

   // Attributes
   // -----------------------------------------------------------------------------------
   private final PacketDecoder packetDecoder;

   private final Connection transportConnection;

   private final Map<Long, Channel> channels = new ConcurrentHashMap<Long, Channel>();

   private final List<FailureListener> failureListeners = new CopyOnWriteArrayList<FailureListener>();

   private final List<CloseListener> closeListeners = new CopyOnWriteArrayList<CloseListener>();

   private final long blockingCallTimeout;

   private final long blockingCallFailoverTimeout;

   private final List<Interceptor> incomingInterceptors;

   private final List<Interceptor> outgoingInterceptors;

   private volatile boolean destroyed;

   private final boolean client;

   private int clientVersion;

   private volatile SimpleIDGenerator idGenerator = new SimpleIDGenerator(CHANNEL_ID.USER.id);

   private boolean idGeneratorSynced = false;

   private final Object transferLock = new Object();

   private final Object failLock = new Object();

   private volatile boolean dataReceived;

   private final Executor executor;

   private volatile boolean executing;

   private final SimpleString nodeID;

   private final long creationTime;

   private String clientID;

   // Constructors
   // ---------------------------------------------------------------------------------

   /*
    * Create a client side connection
    */
   public RemotingConnectionImpl(final PacketDecoder packetDecoder,
                                 final Connection transportConnection,
                                 final long blockingCallTimeout,
                                 final long blockingCallFailoverTimeout,
                                 final List<Interceptor> incomingInterceptors,
                                 final List<Interceptor> outgoingInterceptors)
   {
      this(packetDecoder, transportConnection, blockingCallTimeout, blockingCallFailoverTimeout, incomingInterceptors, outgoingInterceptors, true, null, null);
   }

   /*
    * Create a server side connection
    */
   RemotingConnectionImpl(final PacketDecoder packetDecoder,
                          final Connection transportConnection,
                          final List<Interceptor> incomingInterceptors,
                          final List<Interceptor> outgoingInterceptors,
                          final Executor executor,
                          final SimpleString nodeID)

   {
      this(packetDecoder, transportConnection, -1, -1, incomingInterceptors, outgoingInterceptors, false, executor, nodeID);
   }

   private RemotingConnectionImpl(final PacketDecoder packetDecoder,
                                  final Connection transportConnection,
                                  final long blockingCallTimeout,
                                  final long blockingCallFailoverTimeout,
                                  final List<Interceptor> incomingInterceptors,
                                  final List<Interceptor> outgoingInterceptors,
                                  final boolean client,
                                  final Executor executor,
                                  final SimpleString nodeID)

   {
      this.packetDecoder = packetDecoder;

      this.transportConnection = transportConnection;

      this.blockingCallTimeout = blockingCallTimeout;

      this.blockingCallFailoverTimeout = blockingCallFailoverTimeout;

      this.incomingInterceptors = incomingInterceptors;

      this.outgoingInterceptors = outgoingInterceptors;

      this.client = client;

      this.executor = executor;

      this.nodeID = nodeID;

      this.creationTime = System.currentTimeMillis();
   }




   // RemotingConnection implementation
   // ------------------------------------------------------------

   @Override
   public String toString()
   {
      return "RemotingConnectionImpl [clientID=" + clientID +
             ", nodeID=" +
             nodeID +
             ", transportConnection=" +
             transportConnection +
             "]";
   }

   public Connection getTransportConnection()
   {
      return transportConnection;
   }

   public List<FailureListener> getFailureListeners()
   {
      return new ArrayList<FailureListener>(failureListeners);
   }

   public void setFailureListeners(final List<FailureListener> listeners)
   {
      failureListeners.clear();

      failureListeners.addAll(listeners);
   }

   /**
    * @return the clientVersion
    */
   public int getClientVersion()
   {
      return clientVersion;
   }

   /**
    * @param clientVersion the clientVersion to set
    */
   public void setClientVersion(int clientVersion)
   {
      this.clientVersion = clientVersion;
   }

   public Object getID()
   {
      return transportConnection.getID();
   }

   public String getRemoteAddress()
   {
      return transportConnection.getRemoteAddress();
   }

   public long getCreationTime()
   {
      return creationTime;
   }

   public synchronized Channel getChannel(final long channelID, final int confWindowSize)
   {
      Channel channel = channels.get(channelID);

      if (channel == null)
      {
         channel = new ChannelImpl(this, channelID, confWindowSize, outgoingInterceptors);

         channels.put(channelID, channel);
      }

      return channel;
   }

   public synchronized boolean removeChannel(final long channelID)
   {
      return channels.remove(channelID) != null;
   }

   public synchronized void putChannel(final long channelID, final Channel channel)
   {
      channels.put(channelID, channel);
   }

   public void addFailureListener(final FailureListener listener)
   {
      if (listener == null)
      {
         throw HornetQClientMessageBundle.BUNDLE.failListenerCannotBeNull();
      }
      failureListeners.add(listener);
   }

   public boolean removeFailureListener(final FailureListener listener)
   {
      if (listener == null)
      {
         throw HornetQClientMessageBundle.BUNDLE.failListenerCannotBeNull();
      }

      return failureListeners.remove(listener);
   }

   public void addCloseListener(final CloseListener listener)
   {
      if (listener == null)
      {
         throw HornetQClientMessageBundle.BUNDLE.closeListenerCannotBeNull();
      }

      closeListeners.add(listener);
   }

   public boolean removeCloseListener(final CloseListener listener)
   {
      if (listener == null)
      {
         throw HornetQClientMessageBundle.BUNDLE.closeListenerCannotBeNull();
      }

      return closeListeners.remove(listener);
   }

   public List<CloseListener> removeCloseListeners()
   {
      List<CloseListener> ret = new ArrayList<CloseListener>(closeListeners);

      closeListeners.clear();

      return ret;
   }

   public List<FailureListener> removeFailureListeners()
   {
      List<FailureListener> ret = new ArrayList<FailureListener>(failureListeners);

      failureListeners.clear();

      return ret;
   }

   public void setCloseListeners(List<CloseListener> listeners)
   {
      closeListeners.clear();

      closeListeners.addAll(listeners);
   }

   public HornetQBuffer createBuffer(final int size)
   {
      return transportConnection.createBuffer(size);
   }

   /*
    * This can be called concurrently by more than one thread so needs to be locked
    */
   public void fail(final HornetQException me)
   {
      synchronized (failLock)
      {
         if (destroyed)
         {
            return;
         }

         destroyed = true;
      }

      HornetQClientLogger.LOGGER.connectionFailureDetected(me.getMessage(), me.getType());

      // Then call the listeners
      callFailureListeners(me);

      callClosingListeners();

      internalClose();

      for (Channel channel : channels.values())
      {
         channel.returnBlocking();
      }
   }

   public void destroy()
   {
      synchronized (failLock)
      {
         if (destroyed)
         {
            return;
         }

         destroyed = true;
      }

      internalClose();

      callClosingListeners();
   }

   public void disconnect(final boolean criticalError)
   {
      Channel channel0 = getChannel(0, -1);

      // And we remove all channels from the connection, this ensures no more packets will be processed after this
      // method is
      // complete

      Set<Channel> allChannels = new HashSet<Channel>(channels.values());

      if (!criticalError)
      {
         removeAllChannels();
      }
      else
      {
         // We can't hold a lock if a critical error is happening...
         // as other threads will be holding the lock while hanging on IO
         channels.clear();
      }

      // Now we are 100% sure that no more packets will be processed we can flush then send the disconnect

      if (!criticalError)
      {
         for (Channel channel: allChannels)
         {
            channel.flushConfirmations();
         }
      }

      Packet disconnect = new DisconnectMessage(nodeID);
      channel0.sendAndFlush(disconnect);
   }

   public long generateChannelID()
   {
      return idGenerator.generateID();
   }

   public synchronized void syncIDGeneratorSequence(final long id)
   {
      if (!idGeneratorSynced)
      {
         idGenerator = new SimpleIDGenerator(id);

         idGeneratorSynced = true;
      }
   }

   public long getIDGeneratorSequence()
   {
      return idGenerator.getCurrentID();
   }

   public Object getTransferLock()
   {
      return transferLock;
   }

   public boolean isClient()
   {
      return client;
   }

   public boolean isDestroyed()
   {
      return destroyed;
   }

   public long getBlockingCallTimeout()
   {
      return blockingCallTimeout;
   }

   @Override
   public long getBlockingCallFailoverTimeout()
   {
      return blockingCallFailoverTimeout;
   }

   public boolean checkDataReceived()
   {
      boolean res = dataReceived;

      dataReceived = false;

      return res;
   }

   //We flush any confirmations on the connection - this prevents idle bridges for example
   //sitting there with many unacked messages
   public void flush()
   {
      synchronized (transferLock)
      {
         for (Channel channel : channels.values())
         {
            channel.flushConfirmations();
         }
      }
   }

   public HornetQPrincipal getDefaultHornetQPrincipal()
   {
      return transportConnection.getDefaultHornetQPrincipal();
   }

   // Buffer Handler implementation
   // ----------------------------------------------------

   public void bufferReceived(final Object connectionID, final HornetQBuffer buffer)
   {
      try
      {
         final Packet packet = packetDecoder.decode(buffer);

         if (isTrace)
         {
            HornetQClientLogger.LOGGER.trace("handling packet " + packet);
         }

         if (packet.isAsyncExec() && executor != null)
         {
            executing = true;

            executor.execute(new Runnable()
            {
               public void run()
               {
                  try
                  {
                     doBufferReceived(packet);
                  }
                  catch (Throwable t)
                  {
                     HornetQClientLogger.LOGGER.errorHandlingPacket(t, packet);
                  }

                  executing = false;
               }
            });
         }
         else
         {
            //To prevent out of order execution if interleaving sync and async operations on same connection
            while (executing)
            {
               Thread.yield();
            }

            // Pings must always be handled out of band so we can send pings back to the client quickly
            // otherwise they would get in the queue with everything else which might give an intolerable delay
            doBufferReceived(packet);
         }

         dataReceived = true;
      }
      catch (Exception e)
      {
         HornetQClientLogger.LOGGER.errorDecodingPacket(e);
      }
   }

   private void doBufferReceived(final Packet packet)
   {
      if (ChannelImpl.invokeInterceptors(packet, incomingInterceptors, this) != null)
      {
         return;
      }

      synchronized (transferLock)
      {
         final Channel channel = channels.get(packet.getChannelID());

         if (channel != null)
         {
            channel.handlePacket(packet);
         }
      }
   }

   private void removeAllChannels()
   {
      // We get the transfer lock first - this ensures no packets are being processed AND
      // it's guaranteed no more packets will be processed once this method is complete
      synchronized (transferLock)
      {
         channels.clear();
      }
   }

   private void callFailureListeners(final HornetQException me)
   {
      final List<FailureListener> listenersClone = new ArrayList<FailureListener>(failureListeners);

      for (final FailureListener listener : listenersClone)
      {
         try
         {
            listener.connectionFailed(me, false);
         }
         catch (HornetQInterruptedException interrupted)
         {
            // this is an expected behaviour.. no warn or error here
            HornetQClientLogger.LOGGER.debug("thread interrupted", interrupted);
         }
         catch (final Throwable t)
         {
            // Failure of one listener to execute shouldn't prevent others
            // from
            // executing
            HornetQClientLogger.LOGGER.errorCallingFailureListener(t);
         }
      }
   }

   private void callClosingListeners()
   {
      final List<CloseListener> listenersClone = new ArrayList<CloseListener>(closeListeners);

      for (final CloseListener listener : listenersClone)
      {
         try
         {
            listener.connectionClosed();
         }
         catch (final Throwable t)
         {
            // Failure of one listener to execute shouldn't prevent others
            // from
            // executing
            HornetQClientLogger.LOGGER.errorCallingFailureListener(t);
         }
      }
   }

   private void internalClose()
   {
      // We close the underlying transport connection
      transportConnection.close();

      for (Channel channel : channels.values())
      {
         channel.close();
      }
   }

   public void setClientID(String cID)
   {
      clientID = cID;
   }

   public String getClientID()
   {
      return clientID;
   }
}
TOP

Related Classes of org.hornetq.core.protocol.core.impl.RemotingConnectionImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.