Package com.cloudhopper.mq.broker

Source Code of com.cloudhopper.mq.broker.AsyncRemoteQueueTransfer

package com.cloudhopper.mq.broker;

/*
* #%L
* ch-mq
* %%
* Copyright (C) 2012 Cloudhopper by Twitter
* %%
* Licensed 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.
* #L%
*/

import com.cloudhopper.mq.broker.protocol.MaxTransferAttemptsCountException;
import com.cloudhopper.mq.broker.protocol.MaxTransferCountException;
import com.cloudhopper.mq.broker.protocol.ProtocolConstants;
import com.cloudhopper.mq.broker.protocol.ProtocolFactory;
import com.cloudhopper.mq.broker.protocol.ProtocolParsingException;
import com.cloudhopper.mq.broker.protocol.ProtocolResponseUtil;
import com.cloudhopper.mq.broker.protocol.ProtocolResultCodeException;
import com.cloudhopper.mq.broker.protocol.TransferResponse;
import com.cloudhopper.mq.message.MQMessage;
import com.cloudhopper.mq.transcoder.TranscoderWrapped;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.Response;
import java.io.IOException;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Utility class to encapsulate the transfer of an item to a RemoteQueue via
* an AsyncHttpClient.
*
* @author garth
*/
public class AsyncRemoteQueueTransfer {
    private static final Logger logger = LoggerFactory.getLogger(AsyncRemoteQueueTransfer.class);

    private final AsyncHttpClient http;
    private final DistributedQueueConfiguration dqconfig;
    private final RemoteQueueTransferListener listener;
    private final RemoteQueueInfo remoteQueue;
    private final TransferItem transferItem;

    public AsyncRemoteQueueTransfer(AsyncHttpClient http, DistributedQueueConfiguration dqconfig, RemoteQueueTransferListener listener, RemoteQueueInfo remoteQueue, TransferItem transferItem) {
        this.http = http;
        this.dqconfig = dqconfig;
        this.listener = listener;
        this.remoteQueue = remoteQueue;
        this.transferItem = transferItem;
    }

    private synchronized String getNextRemoteBroker() throws NoMoreRemoteBrokersException {
        // first, let's get the URL we'll try to transfer to
        String remoteBroker = remoteQueue.getNextRemoteBroker();

        // are there any more remote brokers?
        if (remoteBroker == null) {
            throw new NoMoreRemoteBrokersException("No more RemoteBrokers available for RemoteQueue [" + remoteQueue.getName() + "] since call to getNextRemoteBroker() returned null during transfer attempt");
        }

        return remoteBroker;
    }

    private RemoteBrokerInfo getBrokerInfo(String url) {
  // could throw an NPE if the broker has vanished
  return remoteQueue.getRemoteBrokers().get(url).getBroker();
    }

    public void transfer(final TransferResponseHandler handler)
    {
        //
        // enforce max "TransferCount" policy
        // temporarily increase current transfer count value by 1 and see if it
        // would exceed the maximum policy configured for this distributed queue
        //
        byte transferCount = (byte)transferItem.getMessage().getTransferCount();
        if (transferCount+1 > dqconfig.getMaxTransferCount()) {
            handler.onThrowable(new MaxTransferCountException("Max transfer count [" + transferCount + "] reached for item"));
      return;
        }

  // get the URL we'll try to transfer to next
  // NOTE: if there are no more brokers, this will throw NoMoreRemoteBrokersException
  String nextBroker = null;
  try {
      nextBroker = getNextRemoteBroker();
  } catch (NoMoreRemoteBrokersException e) {
      handler.onThrowable(e);
      return;
  }
  final String remoteBrokerUrl = new String(nextBroker);
  final RemoteBrokerInfo brokerInfo = getBrokerInfo(nextBroker);


  //
  // enforce max "TransferAttemptsCount" policy
  //
  if (transferItem.getMessage().getTransferAttemptsCount()+1 > dqconfig.getMaxTransferAttemptsCount()) {
      handler.onThrowable(new MaxTransferAttemptsCountException("Max transfer attempts count [" + transferItem.getMessage().getTransferAttemptsCount() + "] exceeded on this processor for item"));
      return;
  }

  // always increment the transfer attempts count
  short transferAttemptsCount = transferItem.getMessage().incrementAndGetTransferAttemptsCount();

  try {
      logger.trace("[{}] Attempting transfer of item to RemoteBroker [{}] [attempt: {} transferCount: {}]", new Object[] { remoteQueue.getName(), remoteBrokerUrl, transferAttemptsCount, transferCount });
     
      AsyncCompletionHandler<TransferResponse> asyncHandler = new AsyncCompletionHandler<TransferResponse>() {
   
    public TransferResponse onCompleted(Response response) throws Exception {
        try {
      // consume content and process it
      String body = response.getResponseBody();
      // try to parse the body
      TransferResponse transferResponse = ProtocolFactory.parseTransferResponse(body);
      // check if the response is valid
      ProtocolResponseUtil.verifyResultCodeIsOK(transferResponse);
      if (handler != null) handler.onComplete(transferResponse);
      return transferResponse;
        } catch (Exception e) {
      onThrowable(e);
        }
        return null;
    }
   
    public void onThrowable(Throwable t) {
        boolean retryable = true;
        if (t instanceof ProtocolResultCodeException) {
      // remote broker returned a specific error code (but did respond :-))
      // depending on the actual error, we should likely do something special
      ProtocolResultCodeException e = (ProtocolResultCodeException)t;
      if (e.getResultCode() == ProtocolConstants.RC_NO_QUEUE || e.getResultCode() == ProtocolConstants.RC_NO_CONSUMER) {
          logger.warn("[{}] RemoteBroker [{}] returned an error indicating the queue is either gone or no longer has a consumer", remoteQueue.getName(), remoteBrokerUrl);
          if (listener != null) { listener.notifyQueueOnRemoteBrokerIsNoLongerAvailable(remoteBrokerUrl, remoteQueue.getName()); }
      } else if (e.getResultCode() == ProtocolConstants.RC_GROUP_NAME_MISMATCH) {
          // this should really only happen if the remote system was restarted and no longer matches ours
          logger.warn("[{}] The group on RemoteBroker [{}] no longer matches ours", remoteQueue.getName(), remoteBrokerUrl);
          if (listener != null) { listener.notifyRemoteBrokerIsNoLongerAvailable(remoteBrokerUrl, e.getMessage()); }
      } else {
          // any other errors such as ITEM_TYPE_MISMATCH, etc..
          logger.warn("[{}] Result code [{}] from remote broker [{}] was not OK, safest action is to notifyQueueOnRemoteBrokerIsNoLongerAvailable", new Object[] { remoteQueue.getName(), e.getResultCode(), remoteBrokerUrl });
          if (listener != null) { listener.notifyQueueOnRemoteBrokerIsNoLongerAvailable(remoteBrokerUrl, remoteQueue.getName()); }
      }
        } else if (t instanceof ProtocolParsingException) {
      // remote broker return a bad protocol response (yet it was still an HTTP 200 error code)
      // this is the most difficult exception since the item *MAY* have actually
      // been transferred to the remote system -- the safe bet is to just notify the
      // system that the queue is the only thing no longer available
      logger.warn("["+remoteQueue.getName()+"] Unexpected parsing issue with protocol (despite an HTTP 200 status code), safest action is to notifyQueueOnRemoteBrokerIsNoLongerAvailable", t);
      if (listener != null) { listener.notifyQueueOnRemoteBrokerIsNoLongerAvailable(remoteBrokerUrl, remoteQueue.getName()); }
        } else if (t instanceof IOException) {
      // unable to actually connect to to Remote Broker
      logger.warn("["+remoteQueue.getName()+"] Unexpected IO exception, going to notifyRemoteBrokerIsNoLongerAvailable", t);
      if (listener != null) { listener.notifyRemoteBrokerIsNoLongerAvailable(remoteBrokerUrl, t.getMessage()); }
        } else {
      logger.warn("["+remoteQueue.getName()+"] Unexpected exception on transfer. Will not retry.", t);
      if (handler != null) handler.onThrowable(t);
      retryable = false;
        }
        if (retryable) {
      logger.trace("[{}] Retrying request {}", remoteQueue.getName(), transferItem);
      transfer(handler);
        }
    }

      };

      // first, let's locally save the queue name (in case remoteQueue was null)
      String queueName = remoteQueue.getName();
     
      String type = null;
      byte[] data = null;

      // Backwards compatibility for transfers
      logger.trace("RemoteBroker version {}", brokerInfo.getVersion());
      logger.trace("Transcoder is {}", transferItem.getLocalQueue().getTranscoder().getClass().getName());
      logger.trace("Item is {}", transferItem.getItem().getClass().getName());

      if (brokerInfo.getVersion() == ProtocolConstants.VERSION_1_0 &&
    transferItem.getLocalQueue().getTranscoder() instanceof TranscoderWrapped &&
    transferItem.getItem() instanceof MQMessage) {
    logger.trace("RemoteBroker[{}] is version 1", brokerInfo);
    logger.trace("Queue[{}] uses TranscoderWrapped and item is MQMessage", transferItem.getLocalQueue().getName());
    TranscoderWrapped tw = (TranscoderWrapped)transferItem.getLocalQueue().getTranscoder();
    MQMessage mqItem = (MQMessage)transferItem.getItem();
    type = mqItem.getBody().getClass().getCanonicalName();
    data = tw.getBaseTranscoder().encode(mqItem.getBody());
      } else {
    type = transferItem.getItemType().getCanonicalName();
    data = transferItem.getLocalQueue().getTranscoder().encode(transferItem.getItem());
      }

      // create the url
      String cmdurl = remoteBrokerUrl + "?cmd=transfer&queue=" + queueName + "&itemType=" + type;
      if (dqconfig.getGroupName() != null) {
    cmdurl += "&group=" + dqconfig.getGroupName();
      }
     
      // execute the request
      AsyncHttpClient.BoundRequestBuilder builder = http.preparePost(cmdurl);
      builder.setBody(data);
      builder.execute(asyncHandler);
  } catch (Exception e) {
      handler.onThrowable(e);
  }
    }
   
}
TOP

Related Classes of com.cloudhopper.mq.broker.AsyncRemoteQueueTransfer

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.