All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.veraxsystems.vxipmi.connection.queue.MessageQueue Maven / Gradle / Ivy

The newest version!
/*
 * MessageQueue.java 
 * Created on 2011-08-24
 *
 * Copyright (c) Verax Systems 2011.
 * All rights reserved.
 *
 * This software is furnished under a license. Use, duplication,
 * disclosure and all other uses are restricted to the rights
 * specified in the written license agreement.
 */
package com.veraxsystems.vxipmi.connection.queue;

import com.veraxsystems.vxipmi.coding.PayloadCoder;
import com.veraxsystems.vxipmi.connection.Connection;
import com.veraxsystems.vxipmi.connection.ConnectionException;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Queues messages to send and checks for timeouts.
 */
public class MessageQueue extends TimerTask {

    private List queue;
    private int timeout;
    private Timer timer;
    private Connection connection;
    private int lastSequenceNumber;
    private Object lastSequenceNumberLock = new Object();
    private int minSequenceNumber = 1;
    private int sequenceNumberUpperBound = 64;

    private static Logger logger = Logger.getLogger(MessageQueue.class);

    /**
     * Frequency of checking messages for timeouts in ms.
     */
    private static int cleaningFrequency = 500;

    /**
     * Size of the queue determined by IPMI sliding window algorithm
     * specification. 
* When queue size is 16, BMC drops some of the messages under heavy load. */ private static final int QUEUE_SIZE = 8; public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public MessageQueue(Connection connection, int timeout, int minSequenceNumber, int maxSequenceNumber) { this.minSequenceNumber = minSequenceNumber; sequenceNumberUpperBound = maxSequenceNumber + 1; reservedTags = new ArrayList(); lastSequenceNumber = minSequenceNumber - 1; this.connection = connection; queue = new ArrayList(); setTimeout(timeout); timer = new Timer(); timer.schedule(this, cleaningFrequency, cleaningFrequency); } private int incrementSequenceNumber(int currentSequenceNumber) { int newSequenceNumber = (currentSequenceNumber + 1) % sequenceNumberUpperBound; if (newSequenceNumber == 0) { newSequenceNumber = minSequenceNumber; } return newSequenceNumber; } /** * Stops the MessageQueue */ public void tearDown() { timer.cancel(); } private List reservedTags; /** * Check if the tag is reserved. */ private synchronized boolean isReserved(int tag) { return reservedTags.contains(tag); } /** * Reserves given tag for the use of the invoker. * * @param tag * - tag to reserve * @return true if tag was reserved successfully, false otherwise */ private synchronized boolean reserveTag(int tag) { if (isReserved(tag)) { reservedTags.add(tag); return true; } return false; } private synchronized void releaseTag(int tag) { reservedTags.remove((Integer) tag); } /** * Adds request to the queue and generates the tag. * * @return Sequence number of the message if it was added to the * queue, -1 otherwise. The tag used to identify message is equal to * that value. */ public int add(PayloadCoder request) { run(); boolean first = true; synchronized (queue) { synchronized (lastSequenceNumberLock) { if (queue.size() < QUEUE_SIZE) { int sequenceNumber = incrementSequenceNumber(lastSequenceNumber); while (isReserved(sequenceNumber)) { sequenceNumber = incrementSequenceNumber(sequenceNumber); if (!first) { try { lastSequenceNumberLock.wait(1); } catch (InterruptedException e) { // TODO log } } if (sequenceNumber == lastSequenceNumber) { //we checked all available sequence numbers, so return -1 (no available sequence numbers) return -1; } first = false; } reserveTag(sequenceNumber); lastSequenceNumber = sequenceNumber; QueueElement element = new QueueElement(sequenceNumber, request); queue.add(element); return sequenceNumber; } } } return -1; } /** * Removes message with the given tag from the queue. */ public void remove(int tag) { run(); synchronized (queue) { int i = 0; int index = -1; for (QueueElement element : queue) { if (element.getId() == tag) { index = i; break; } ++i; } if (index == 0) { queue.remove(0); releaseTag(tag); while (!queue.isEmpty() && queue.get(0).getRequest() == null) { int additionalTag = queue.get(0).getId(); queue.remove(0); releaseTag(additionalTag); } } else if (index > 0) { queue.get(index).setRequest(null); } } } /** * Removes message from queue at given index. * * @param index */ public void removeAt(int index) { if (index >= queue.size()) { throw new IndexOutOfBoundsException("Index out of bounds : " + index); } remove(queue.get(index).getId()); } /** * Checks if queue contains message with the given sequence number. */ public boolean containsId(int sequenceNumber) { synchronized (queue) { for (QueueElement element : queue) { if (element.getId() == sequenceNumber && element.getRequest() != null) { return true; } } } return false; } /** * Returns valid session sequence number that cannot be used as a tag though */ public int getSequenceNumber() { synchronized (lastSequenceNumberLock) { int sequenceNumber = incrementSequenceNumber(lastSequenceNumber); lastSequenceNumber = sequenceNumber; return sequenceNumber; } } /** * Returns message with the given sequence number from the queue or null if * no message with the given tag is currently in the queue. */ public PayloadCoder getMessageFromQueue(int tag) { synchronized (queue) { for (QueueElement element : queue) { if (element.getId() == tag && element.getRequest() != null) { return element.getRequest(); } } } return null; } /** * Returns index of the message with the given sequence number from the * queue or -1 if no message with the given tag is currently in the queue. */ public int getMessageIndexFromQueue(int tag) { synchronized (queue) { int i = 0; for (QueueElement element : queue) { if (element.getId() == tag && element.getRequest() != null) { return i; } ++i; } } return -1; } /** * Returns number of retries that were performed on message tagged with tag * or -1 if no such message can be found in the queue. * * @deprecated retries are no longer supported on the message level */ @Deprecated public int getMessageRetries(int tag) { synchronized (queue) { for (QueueElement element : queue) { if (element.getId() == tag && element.getRequest() != null) { return element.getRetries(); } } } return -1; } /** * Returns the ID of the {@link QueueElement} in the queue with the given * tag. * * @param tag * Tag of the message to find */ public int getMessageSequenceNumber(int tag) { synchronized (queue) { for (QueueElement element : queue) { if (element.getId() == tag && element.getRequest() != null) { return element.getId(); } } } return -1; } /** * {@link TimerTask} runner - periodically checks queue for timed out * messages. */ @Override public void run() { if (queue != null) { synchronized (queue) { boolean process = true; while (process && !queue.isEmpty()) { QueueElement oldestQueueElement = queue.get(0); boolean done = oldestQueueElement.getRequest() == null; if (messageJustTimedOut(oldestQueueElement) || done) { processObsoleteMessage(oldestQueueElement, done); } else { process = false; } } } } } private boolean messageJustTimedOut(QueueElement oldestQueueElement) { Date now = new Date(); return now.getTime() - oldestQueueElement.getTimestamp().getTime() > (long) timeout; } private void processObsoleteMessage(QueueElement message, boolean done) { int tag = message.getId(); boolean previouslyTimedOut = message.isTimedOut(); if (previouslyTimedOut || done) { queue.remove(0); logger.info("Removing message after timeout, tag: " + tag); releaseTag(tag); } else { message.makeTimedOut(); message.refreshTimestamp(); connection.notifyResponseListeners(connection.getHandle(), tag, null, new ConnectionException("Message timed out")); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy