Package com.linkedin.databus.core.async

Source Code of com.linkedin.databus.core.async.AbstractActorMessageQueue$MessageQueueFilter

package com.linkedin.databus.core.async;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.
*
*/


import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.apache.log4j.Logger;

import com.linkedin.databus.core.DatabusComponentStatus;
import com.linkedin.databus2.core.BackoffTimerStaticConfig;

/**
* A default implementation for an actor which let it be run in a thread and provides
* controls for its lifecycle.
*/
public abstract class AbstractActorMessageQueue implements Runnable, ActorMessageQueue
{
  public static final String MODULE = AbstractActorMessageQueue.class.getName();
  public final Logger _log;

  public static final int MAX_QUEUED_MESSAGE_HISTORY_SIZE = 100;
  public static final int MAX_QUEUED_MESSAGES = 10;
  public static final long MESSAGE_QUEUE_POLL_TIMEOUT_MS = 100;

  private final String _name;
  private final Queue<Object> _messageQueue = new ArrayDeque<Object>(MAX_QUEUED_MESSAGES);
  protected final CircularFifoBuffer _messageProcessedHistory = new CircularFifoBuffer(MAX_QUEUED_MESSAGE_HISTORY_SIZE);
  volatile boolean _hasMessages;

  private final Lock _controlLock = new ReentrantLock(true);
  private final Condition _shutdownCondition = _controlLock.newCondition();
  private final Condition _newStateCondition = _controlLock.newCondition();
  private volatile LifecycleMessage _shutdownRequest = null;
  protected LifecycleMessage _currentLifecycleState;
  protected final DatabusComponentStatus _componentStatus;

  private final MessageQueueFilter pauseFilter = new DefaultPauseFilter();
  private final MessageQueueFilter suspendFilter = new DefaultSuspendFilter();
  private final MessageQueueFilter shutdownFilter = new DefaultShutdownFilter();

  private long _numEnqueuedMessages = 0;
  private final boolean _enablePullerMessageQueueLogging;

  public AbstractActorMessageQueue(String name, BackoffTimerStaticConfig errorRetriesConf)
  {
    this(name, errorRetriesConf,false,null);
  }

  public AbstractActorMessageQueue(String name,
                     BackoffTimerStaticConfig errorRetriesConf,
                     boolean enablePullerMessageQueueLogging,
                     Logger log)
  {
    super();
    _name = name;
    _currentLifecycleState = null;
    _componentStatus = new DatabusComponentStatus(name, errorRetriesConf);
    _enablePullerMessageQueueLogging = enablePullerMessageQueueLogging;
    _hasMessages = false;
    if (null != log)
    {
      _log = log;
    }
    else
    {
      // Should happen only for unit-tests
      _log = Logger.getLogger(MODULE);
    }
  }

  // Used only for unit-testing. Ideally moved to a factory method and reduce scope to
  // be private. But there are unit tests which inherit from this class and require
  // simple super() methods

  protected AbstractActorMessageQueue(String name)
  {
    this(name,BackoffTimerStaticConfig.UNLIMITED_RETRIES);
  }

  /**
   * This method is called by the StatMachine thread after it exits out of the main loop in AbstractActorMessageQueue.run() and
   * is about to shutdown.
   * After this method returns, the State Machine's status will be set to SHUTDOWN and other waiting threads (for this shutdown) will be signaled.
   * Subclasses must implement this method to cleanup their state for shutdown
   */
  protected abstract void onShutdown();

  /**
   * This method is called by the StatMachine thread after it exits out of the main loop in AbstractActorMessageQueue.run()
   */
  private void performShutdown()
  {
    onShutdown();
    _log.info(getName() + " shutdown.");
    clearQueue(shutdownFilter);
  }

  protected void onResume()
  {
  }

  public final boolean doExecuteAndChangeState(Object message)
  {
    boolean success = false;

    try
    {
      success = executeAndChangeState(message);
    } catch (RuntimeException re) {
      _log.error("Stopping because of runtime exception :", re);
      success = false;
    }

    _messageProcessedHistory.add(message);
    _numEnqueuedMessages++;

    if ( ! success )
    {
      _log.info("Message Queue History (earliest first) at end:" + getMessageHistoryLog());
    } else if (_numEnqueuedMessages%MAX_QUEUED_MESSAGE_HISTORY_SIZE == 0) {
      if (_enablePullerMessageQueueLogging)
      {
        _log.info("Message Queue History (earliest first) :" + getMessageHistoryLog());
      } else if (_log.isDebugEnabled()) {
        _log.debug("Message Queue History (earliest first) :" + getMessageHistoryLog());
      }
    }

    return success;
  }


  protected boolean executeAndChangeState(Object message)
  {
    boolean success = true;

    if (message instanceof LifecycleMessage)
    {
      LifecycleMessage lcMessage = (LifecycleMessage)message;

      switch (lcMessage.getTypeId())
      {
        case START: doStart(lcMessage); break;
        case PAUSE: doPause(lcMessage); break;
        case SUSPEND_ON_ERROR: doSuspendOnError(lcMessage); break;
        case RESUME: doResume(lcMessage); break;
        case SHUTDOWN:
        {
          _log.error("Shutdown message is seen in the queue but not expected : Message :" + lcMessage);
          success = false;
          break;
        }
        default:
        {
          _log.error("Unknown Lifecycle message in RelayPullThread: " + lcMessage.getTypeId());
          success = false;
          break;
        }
      }
    }
    else
    {
      _log.error("Unknown message of type " + message.getClass().getName() + ": " + message.toString());
      success = false;
    }

    return success;
  }

  protected void doResume(LifecycleMessage lcMessage)
  {
    _log.info(getName() + ": resuming");
    _componentStatus.resume();
    onResume();
  }

  protected void doSuspendOnError(LifecycleMessage lcMessage)
  {
    final Throwable lastError = lcMessage.getLastError();
    if (null != lastError)
    {
      _log.info(getName() + ": suspending due to " + lastError, lastError);
    }
    else
    {
      _log.info(getName() + ": suspending");
    }
   if (_log.isDebugEnabled())
     _log.debug(" because of message: " + lcMessage.getLastError());
    _componentStatus.suspendOnError(lcMessage.getLastError());
    clearQueue(suspendFilter);
  }

  protected void doPause(LifecycleMessage lcMessage)
  {
    _log.info(getName() + ": pausing");
    _componentStatus.pause();
    clearQueue(pauseFilter);
  }

  protected void doStart(LifecycleMessage lcMessage)
  {
    _log.info(getName() + ": starting");
    _componentStatus.start();
  }

  @Override
  public void run()
  {
    boolean isDebugEnabled = _log.isDebugEnabled();

    Object nextState = null;

    boolean running = true;

    try
    {
      while (running && !checkForShutdownRequest())
      {
        nextState = pollNextState();
        if (null == nextState)
        {
          running = false;
        }
        else
        {
          if (isDebugEnabled) _log.debug(getName() + ": new state: " + nextState.toString());
          running = doExecuteAndChangeState(nextState);
        }
      }
    }
    catch (Exception e)
    {
      _log.error(getName() + ": stopping because of unhandled exception: ", e);
      running = false;
    }

    if (isDebugEnabled)
    {
        StringBuilder sb = new StringBuilder(10240);
        sb.append(getName());
        sb.append(": message queue at exit:");
        while (null != (nextState = _messageQueue.poll()))
        {
            sb.append(nextState.toString());
            sb.append(' ');
        }

        _log.debug(sb.toString());
    }

    try
    {
      performShutdown();
    } finally {
      _controlLock.lock();
      try
      {
        _componentStatus.shutdown();
        _shutdownCondition.signalAll();
      }
      finally
      {
        _controlLock.unlock();
      }

      _log.info("Message Queue History (earliest first) at shutdown:" + getMessageHistoryLog());
      if (isDebugEnabled) _log.debug(getName() + ": exited message loop.");
    }
  }

  /*
   * Atomically filters the message queue and enqueues the passed message
   */
  public void enqueueMessageAfterFilter(Object message, MessageQueueFilter filter)
  {
    try
    {
      _controlLock.lock();
      clearQueue(filter);
      enqueueMessage(message);
    } finally {
      _controlLock.unlock();
    }
  }

  /**
   * Preprocess message before enqueueing
   *
   * @param message Message to be enqueued
   * @return processed message
   */
  protected Object preEnqueue(Object message)
  {
    return message;
  }

  @Override
  public void enqueueMessage(Object message)
  {
    if (null == message)
    {
      _log.warn("Attempt to queue empty state");
      return;
    }

    _controlLock.lock();

    try
    {
      message = preEnqueue(message);

      if (_componentStatus.getStatus() == DatabusComponentStatus.Status.SHUTDOWN)
      {
        _log.warn(getName() + ": shutdown: ignoring " + message.toString());
      }
      else if (checkForShutdownRequest())
      {
        _log.warn(getName() + ": shutdown requested: ignoring " + message.toString());
      }
      else if ((_componentStatus.getStatus() == DatabusComponentStatus.Status.PAUSED&&
                  (! shouldRetainMessageOnPause(message)))
      {
        _log.warn(getName() + ": ignoring message while paused: " + message.toString());
      }
      else if ((_componentStatus.getStatus() == DatabusComponentStatus.Status.SUSPENDED_ON_ERROR&&
              (! shouldRetainMessageOnSuspend(message)))
      {
        _log.warn(getName() + ": ignoring message while suspended_on_error: " + message.toString());
      }
      else
      {
        boolean offerSuccess = _messageQueue.offer(message);
        if (!offerSuccess) _log.error(getName() + ": adding a new state failed: " + message.toString()
                                     + "; queue.size=" + _messageQueue.size());

        if (1 == _messageQueue.size()) _newStateCondition.signalAll();
        _hasMessages = true;
      }

//      LOG.info(getName() + ": " + _messageQueue.toString());
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  public void shutdown()
  {
    _log.info(getName() + ": shutdown requested.");
    _shutdownRequest = LifecycleMessage.createShutdownMessage();
  }

  public void awaitShutdown()
  {
    _log.info(getName() + ": waiting for shutdown" );
    _controlLock.lock();
    try
    {
      _log.info(getName() + ": status at shutdown: " + _componentStatus.getStatus());
      _log.info(getName() + ": queue at shutdown: " + _messageQueue);
      while (_componentStatus.getStatus() != DatabusComponentStatus.Status.SHUTDOWN &&
             _componentStatus.getStatus() != DatabusComponentStatus.Status.INITIALIZING)
      {
        _shutdownCondition.awaitUninterruptibly();
      }
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  public String getName()
  {
    return _name;
  }

  public boolean isShutdown()
  {
    _controlLock.lock();
    try
    {
      return _componentStatus.getStatus() == DatabusComponentStatus.Status.SHUTDOWN;
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  private Object pollNextState()
  {
    Object nextState = null;

    _controlLock.lock();
    try
    {
      while (! checkForShutdownRequest() && _messageQueue.isEmpty())
      {
        try
        {
          _newStateCondition.await(MESSAGE_QUEUE_POLL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
        }
        catch (InterruptedException ie){}
      }
      if (! checkForShutdownRequest())
      {
        nextState = _messageQueue.poll();
        _hasMessages = _messageQueue.size() > 0;
      }
    }
    finally
    {
      _controlLock.unlock();
    }

    return nextState;
  }

  public DatabusComponentStatus getComponentStatus()
  {
    return _componentStatus;
  }

  public boolean checkForShutdownRequest()
  {
    return null != _shutdownRequest;
  }

  public String getQueueListString()
  {
    StringBuilder sb = new StringBuilder(100);
    getQueueListString(sb);

    return sb.toString();
  }

  public void getQueueListString(StringBuilder sb)
  {
    _controlLock.lock();
    try
    {
      sb.append(getName());
      sb.append(" queue: ");
      sb.append(_messageQueue.toString());
    }
    finally
    {
      _controlLock.unlock();
    }
  }

  protected void clearQueue(MessageQueueFilter filter)
  {
    try
    {
      _controlLock.lock();

      Iterator<Object> itr = _messageQueue.iterator();

      while (itr.hasNext())
      {
        Object msg = itr.next();

        boolean retain = filter.shouldRetain(msg);

        if (! retain)
          itr.remove();
      }
    } finally {
      _controlLock.unlock();
    }
  }

  /**
   * By default, all messages except lifecycle messages are cleared on pause
   */
  protected void clearMessageQueueOnPause()
  {
    clearQueue(new DefaultPauseFilter());
  }

  /**
   * By default, all messages except life-cycle messages are cleared on Suspend_On_Error
   */
  protected void clearMessageQueueOnSuspend()
  {
    clearQueue(new DefaultSuspendFilter());
  }

  /**
   * By default, all messages are cleared on shutdown
   */
  protected void clearMessageQueueOnShutdown()
  {
    clearQueue(new DefaultShutdownFilter());
  }


  /**
   * Filter Interface for clearing Message Queue
   */
  public interface MessageQueueFilter
  {
    public boolean shouldRetain(Object msg);
  }

  /**
   * Filter for clearing Message Queue on shutdown
   */
  private class DefaultPauseFilter implements MessageQueueFilter
  {

  @Override
  public boolean shouldRetain(Object msg)
  {
    return shouldRetainMessageOnPause(msg);
  }
  }

  /**
   * Filter for clearing Message Queue on Suspend_on_Error
   */
  private class DefaultSuspendFilter implements MessageQueueFilter
  {
  @Override
  public boolean shouldRetain(Object msg)
  {
    return shouldRetainMessageOnSuspend(msg);
  }
  }

  /**
   * Filter for clearing Message Queue on shutdown
   */
  private class DefaultShutdownFilter implements MessageQueueFilter
  {
  @Override
  public boolean shouldRetain(Object msg) {
    return shouldRetainMessageOnShutdown(msg);
  }
  }

  /**
   * By default, all messages except lifecycle messages are cleared on pause
   */
  protected boolean shouldRetainMessageOnPause(Object msg)
  {
    if ( msg instanceof LifecycleMessage)
    return true;

    return false;
  }


  /**
   * By default, all messages except lifecycle messages are cleared on suspend
   */
  protected boolean shouldRetainMessageOnSuspend(Object msg)
  {
    if ( msg instanceof LifecycleMessage)
    return true;

    return false;
  }


  /**
   * By default, all messages are cleared on shutdown
   */
  protected boolean shouldRetainMessageOnShutdown(Object msg)
  {
    return false;
  }

  public Queue<Object> getMessageQueue()
  {
  return _messageQueue;
  }

  public String getMessageHistoryLog()
  {
    return _messageProcessedHistory.toString();
  }

  protected boolean hasMessages()
  {
    return _hasMessages;
  }

}
TOP

Related Classes of com.linkedin.databus.core.async.AbstractActorMessageQueue$MessageQueueFilter

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.