Package org.hornetq.core.protocol.stomp

Source Code of org.hornetq.core.protocol.stomp.StompConnection

/*
* Copyright 2010 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.stomp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList;

import org.hornetq.api.core.HornetQBuffer;
import org.hornetq.api.core.HornetQBuffers;
import org.hornetq.api.core.HornetQException;
import org.hornetq.api.core.client.HornetQClient;
import org.hornetq.core.protocol.stomp.v10.StompFrameHandlerV10;
import org.hornetq.core.protocol.stomp.v12.StompFrameHandlerV12;
import org.hornetq.core.remoting.CloseListener;
import org.hornetq.core.remoting.FailureListener;
import org.hornetq.core.remoting.impl.netty.TransportConstants;
import org.hornetq.core.server.HornetQServerLogger;
import org.hornetq.core.server.ServerMessage;
import org.hornetq.core.server.impl.ServerMessageImpl;
import org.hornetq.spi.core.protocol.RemotingConnection;
import org.hornetq.spi.core.remoting.Acceptor;
import org.hornetq.spi.core.remoting.Connection;
import org.hornetq.utils.ConfigurationHelper;
import org.hornetq.utils.VersionLoader;

/**
* A StompConnection
* @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a>
*/
public final class StompConnection implements RemotingConnection
{
   protected static final String CONNECTION_ID_PROP = "__HQ_CID";
   private static final String SERVER_NAME = "HornetQ/" + VersionLoader.getVersion().getFullVersion() +
            " HornetQ Messaging Engine";

   private final StompProtocolManager manager;

   private final Connection transportConnection;

   private String login;

   private String passcode;

   private String clientID;

   //this means login is valid. (stomp connection ok)
   private boolean valid;

   private boolean destroyed = false;

   private final long creationTime;

   private final Acceptor acceptorUsed;

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

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

   private final Object failLock = new Object();

   private boolean dataReceived;

   private final boolean enableMessageID;

   private StompVersions version;

   private VersionedStompFrameHandler frameHandler;

   //this means the version negotiation done.
   private boolean initialized;

   private FrameEventListener stompListener;

   private final Object sendLock = new Object();

   private int minLargeMessageSize;

   public StompFrame decode(HornetQBuffer buffer) throws HornetQStompException
   {
      StompFrame frame = null;
      try
      {
         frame = frameHandler.decode(buffer);
      }
      catch (HornetQStompException e)
      {
         switch (e.getCode())
         {
         case HornetQStompException.INVALID_EOL_V10:
            if (version != null) throw e;
            frameHandler = new StompFrameHandlerV12(this);
            buffer.resetReaderIndex();
            frame = decode(buffer);
            break;
         case HornetQStompException.INVALID_COMMAND:
            frameHandler.onError(e);
            break;
         default:
            throw e;
         }
      }
      return frame;
   }
  
   public boolean hasBytes()
   {
      return frameHandler.hasBytes();
   }

   StompConnection(final Acceptor acceptorUsed, final Connection transportConnection, final StompProtocolManager manager)
   {
      this.transportConnection = transportConnection;

      this.manager = manager;

      this.frameHandler = new StompFrameHandlerV10(this);

      this.creationTime = System.currentTimeMillis();

      this.acceptorUsed = acceptorUsed;

      this.enableMessageID = ConfigurationHelper.getBooleanProperty(TransportConstants.STOMP_ENABLE_MESSAGE_ID,
                                                                 false,
                                                                 acceptorUsed.getConfiguration());
      this.minLargeMessageSize = ConfigurationHelper.getIntProperty(TransportConstants.STOMP_MIN_LARGE_MESSAGE_SIZE,
                                                                                 HornetQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE,
                                                                                 acceptorUsed.getConfiguration());
   }

   @Override
   public void addFailureListener(final FailureListener listener)
   {
      if (listener == null)
      {
         throw new IllegalStateException("FailureListener cannot be null");
      }

      failureListeners.add(listener);
   }

   @Override
   public boolean removeFailureListener(final FailureListener listener)
   {
      if (listener == null)
      {
         throw new IllegalStateException("FailureListener cannot be null");
      }

      return failureListeners.remove(listener);
   }

   @Override
   public void addCloseListener(final CloseListener listener)
   {
      if (listener == null)
      {
         throw new IllegalStateException("CloseListener cannot be null");
      }

      closeListeners.add(listener);
   }

   @Override
   public boolean removeCloseListener(final CloseListener listener)
   {
      if (listener == null)
      {
         throw new IllegalStateException("CloseListener cannot be null");
      }

      return closeListeners.remove(listener);
   }

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

      closeListeners.clear();

      return ret;
   }

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

      failureListeners.clear();

      return ret;
   }

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

      closeListeners.addAll(listeners);
   }

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

      failureListeners.addAll(listeners);
   }

   protected synchronized void setDataReceived()
   {
      dataReceived = true;
   }

   public synchronized boolean checkDataReceived()
   {
      boolean res = dataReceived;

      dataReceived = false;

      return res;
   }

   public void checkDestination(String destination) throws HornetQStompException
   {
      if (!manager.destinationExists(destination))
      {
         throw new HornetQStompException("Destination doesn't exist: " + destination);
      }
   }

   @Override
   public HornetQBuffer createBuffer(int size)
   {
      return HornetQBuffers.dynamicBuffer(size);
   }

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

      destroyed = true;

      internalClose();

      synchronized(sendLock)
      {
         callClosingListeners();
      }
   }

   Acceptor getAcceptorUsed()
   {
      return acceptorUsed;
   }

   private void internalClose()
   {
      transportConnection.close();

      manager.cleanup(this);
   }

   public void fail(final HornetQException me)
   {
      synchronized (failLock)
      {
         if (destroyed)
         {
            return;
         }

         destroyed = true;
      }

      HornetQServerLogger.LOGGER.connectionFailureDetected(me.getMessage(), me.getType());
      // Then call the listeners
      callFailureListeners(me);

      callClosingListeners();

      internalClose();
   }

   public void flush()
   {
   }

   public List<FailureListener> getFailureListeners()
   {
      // we do not return the listeners otherwise the remoting service
      // would NOT destroy the connection.
      return Collections.emptyList();
   }

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

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

   public long getCreationTime()
   {
      return creationTime;
   }

   public Connection getTransportConnection()
   {
      return transportConnection;
   }

   public boolean isClient()
   {
      return false;
   }

   public boolean isDestroyed()
   {
      return destroyed;
   }

   public void bufferReceived(Object connectionID, HornetQBuffer buffer)
   {
      manager.handleBuffer(this, buffer);
   }

   public String getLogin()
   {
      return login;
   }

   public String getPasscode()
   {
      return passcode;
   }

   public void setClientID(String clientID)
   {
      this.clientID = clientID;
   }

   public String getClientID()
   {
      return clientID;
   }

   public boolean isValid()
   {
      return valid;
   }

   public void setValid(boolean valid)
   {
      this.valid = valid;
   }

   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 (final Throwable t)
         {
            // Failure of one listener to execute shouldn't prevent others
            // from
            // executing
            HornetQServerLogger.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
            HornetQServerLogger.LOGGER.errorCallingFailureListener(t);
         }
      }
   }

   /*
    * accept-version value takes form of "v1,v2,v3..."
    * we need to return the highest supported version
    */
   public void negotiateVersion(StompFrame frame) throws HornetQStompException
   {
      String acceptVersion = frame.getHeader(Stomp.Headers.ACCEPT_VERSION);

      if (acceptVersion == null)
      {
         this.version = StompVersions.V1_0;
      }
      else
      {
         Set<String> requestVersions = new HashSet<String>();
         StringTokenizer tokenizer = new StringTokenizer(acceptVersion, ",");
         while (tokenizer.hasMoreTokens())
         {
            requestVersions.add(tokenizer.nextToken());
         }

         if (requestVersions.contains("1.2"))
         {
            this.version = StompVersions.V1_2;
         }
         else if (requestVersions.contains("1.1"))
         {
            this.version = StompVersions.V1_1;
         }
         else if (requestVersions.contains("1.0"))
         {
            this.version = StompVersions.V1_0;
         }
         else
         {
            //not a supported version!
            HornetQStompException error = new HornetQStompException("Stomp versions not supported: " + acceptVersion);
            error.addHeader("version", acceptVersion);
            error.addHeader("content-type", "text/plain");
            error.setBody("Supported protocol version are " + manager.getSupportedVersionsAsString());
            error.setDisconnect(true);
            throw error;
         }
      }
     

      if (this.version != (StompVersions.V1_0))
      {
         VersionedStompFrameHandler newHandler = VersionedStompFrameHandler.getHandler(this, this.version);
         newHandler.initDecoder(this.frameHandler);
         this.frameHandler = newHandler;
      }
      this.initialized = true;
   }

   //reject if the host doesn't match
   public void setHost(String host) throws HornetQStompException
   {
      if (host == null)
      {
         HornetQStompException error = new HornetQStompException("Header host is null");
         error.setBody("Cannot accept null as host");
         throw error;
      }

      String localHost = manager.getVirtualHostName();
      if (!host.equals(localHost))
      {
         HornetQStompException error = new HornetQStompException("Header host doesn't match server host");
         error.setBody("host " + host + " doesn't match server host name");
         throw error;
      }
   }

   public void handleFrame(StompFrame request)
   {
      StompFrame reply = null;

      if (stompListener != null)
      {
         stompListener.requestAccepted(request);
      }

      String cmd = request.getCommand();
      try
      {
         if (isDestroyed())
         {
            throw new HornetQStompException("Connection was destroyed.");
         }
         if (!initialized)
         {
            if ( ! (Stomp.Commands.CONNECT.equals(cmd) || Stomp.Commands.STOMP.equals(cmd)))
            {
               throw new HornetQStompException("Connection hasn't been established.");
            }
            //decide version
            negotiateVersion(request);
         }
        
         reply = frameHandler.handleFrame(request);
      }
      catch (HornetQStompException e)
      {
         reply = e.getFrame();
      }

      if (reply != null)
      {
         sendFrame(reply);
      }

      if (Stomp.Commands.DISCONNECT.equals(cmd))
      {
         this.disconnect(false);
      }
   }

   public void sendFrame(StompFrame frame)
   {
      manager.sendReply(this, frame);
   }

   public boolean validateUser(final String login1, final String passcode1)
   {
      this.valid = manager.validateUser(login1, passcode1);
      if (valid)
      {
         this.login = login1;
         this.passcode = passcode1;
      }
      return valid;
   }

   public ServerMessageImpl createServerMessage()
   {
      return manager.createServerMessage();
   }

   public StompSession getSession(String txID) throws HornetQStompException
   {
      StompSession session = null;
      try
      {
         if (txID == null)
         {
            session = manager.getSession(this);
         }
         else
         {
            session = manager.getTransactedSession(this, txID);
         }
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Exception getting session", e);
      }

      return session;
   }

   protected void validate() throws HornetQStompException
   {
      if (!this.valid)
      {
         throw new HornetQStompException("Connection is not valid.");
      }
   }

   protected void sendServerMessage(ServerMessageImpl message, String txID) throws HornetQStompException
   {
      StompSession stompSession = getSession(txID);

      if (stompSession.isNoLocal())
      {
         message.putStringProperty(CONNECTION_ID_PROP, getID().toString());
      }
      if (enableMessageID())
      {
         message.putStringProperty("hqMessageId",
               "STOMP" + message.getMessageID());
      }
      try
      {
         if (minLargeMessageSize == -1 || (message.getBodyBuffer().writerIndex() < minLargeMessageSize))
         {
            stompSession.sendInternal(message, true);
         }
         else
         {
            stompSession.sendInternalLarge(message, true);
         }
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error sending message " + message, e);
      }
   }

   @Override
   public void disconnect(final boolean criticalError)
   {
      destroy();
   }

   protected void beginTransaction(String txID) throws HornetQStompException
   {
      try
      {
         manager.beginTransaction(this, txID);
      }
      catch (HornetQStompException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error beginning a transaction: " + txID, e);
      }
   }

   public void commitTransaction(String txID) throws HornetQStompException
   {
      try
      {
         manager.commitTransaction(this, txID);
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error committing " + txID, e);
      }
   }

   public void abortTransaction(String txID) throws HornetQStompException
   {
      try
      {
         manager.abortTransaction(this, txID);
      }
      catch (HornetQStompException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error aborting " + txID, e);
      }
   }

   void subscribe(String destination, String selector, String ack,
         String id, String durableSubscriptionName, boolean noLocal) throws HornetQStompException
   {
      if (noLocal)
      {
         String noLocalFilter = CONNECTION_ID_PROP + " <> '" + getID().toString() + "'";
         if (selector == null)
         {
            selector = noLocalFilter;
         }
         else
         {
            selector += " AND " + noLocalFilter;
         }
      }

      if (ack == null)
      {
         ack = Stomp.Headers.Subscribe.AckModeValues.AUTO;
      }

      String subscriptionID = null;
      if (id != null)
      {
         subscriptionID = id;
      }
      else
      {
         if (destination == null)
         {
            throw new HornetQStompException("Client must set destination or id header to a SUBSCRIBE command");
         }
         subscriptionID = "subscription/" + destination;
      }

      try
      {
         manager.createSubscription(this, subscriptionID, durableSubscriptionName, destination, selector, ack, noLocal);
      }
      catch (HornetQStompException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error creating subscription " + subscriptionID, e);
      }
   }

   public void unsubscribe(String subscriptionID) throws HornetQStompException
   {
      try
      {
         manager.unsubscribe(this, subscriptionID);
      }
      catch (HornetQStompException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error unsubscripting " + subscriptionID, e);
      }
   }

   public void acknowledge(String messageID, String subscriptionID) throws HornetQStompException
   {
      try
      {
         manager.acknowledge(this, messageID, subscriptionID);
      }
      catch (HornetQStompException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new HornetQStompException("Error acknowledging message " + messageID, e);
      }
   }

   public String getVersion()
   {
      return String.valueOf(version);
   }

   public String getHornetQServerName()
   {
      return SERVER_NAME;
   }

   public StompFrame createStompMessage(ServerMessage serverMessage,
         StompSubscription subscription, int deliveryCount) throws Exception
   {
      return frameHandler.createMessageFrame(serverMessage, subscription, deliveryCount);
   }

   public void addStompEventListener(FrameEventListener listener)
   {
      this.stompListener = listener;
   }

   //send a ping stomp frame
   public void ping(StompFrame pingFrame)
   {
      manager.sendReply(this, pingFrame);
   }

   public void physicalSend(StompFrame frame) throws Exception
   {
      HornetQBuffer buffer = frame.toHornetQBuffer();
      synchronized (sendLock)
      {
         getTransportConnection().write(buffer, false, false);
      }

      if (stompListener != null)
      {
         stompListener.replySent(frame);
      }

   }

   public VersionedStompFrameHandler getFrameHandler()
   {
      return this.frameHandler;
   }

   public boolean enableMessageID()
   {
      return enableMessageID;
   }

   public int getMinLargeMessageSize()
   {
      return minLargeMessageSize;
   }

}
TOP

Related Classes of org.hornetq.core.protocol.stomp.StompConnection

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.