package com.cloudhopper.mq.broker;
/*
* #%L
* ch-mq-remote
* %%
* Copyright (C) 2009 - 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.queue.Queue;
import com.cloudhopper.mq.queue.QueueManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Fair scheduler implementing QueueListener and BlockingQueue. Uses an internal blocking queue
* implementation that doesn't allow duplicates to schedule work.
*
* @author garth
*/
public class FairRemoteQueueTransferScheduler
extends RemoteQueueTransferScheduler
{
private static final Logger logger = LoggerFactory.getLogger(FairRemoteQueueTransferScheduler.class);
private final NoDupBlockingQueue<Queue> workList;
public FairRemoteQueueTransferScheduler(int maxConcurrentRequests, QueueManager queueManager) {
super(maxConcurrentRequests, queueManager);
this.workList = new NoDupBlockingQueue<Queue>();
}
public void addLocalToRemoteQueueProcessor(Queue queue, LocalToRemoteQueueProcessor processor) {
super.addLocalToRemoteQueueProcessor(queue, processor);
add(queue);
}
public void removeLocalToRemoteQueueProcessor(Queue queue) {
remove(queue);
super.removeLocalToRemoteQueueProcessor(queue);
}
protected synchronized Runnable getNextRemoteTransfer() throws InterruptedException {
logger.trace("getNextRemoteTransfer()");
RemotingQueueDrain r = null;
do {
Queue q = workList.take();
if (q != null) {
if (processors.get(q) != null) {
logger.trace("[{}] Queue from getNext(), returning RemotingQueueDrain.", q.getName());
r = new RemotingQueueDrain(q, processors.get(q), maxConcurrentRequests, this);
} else {
logger.trace("[{}] Queue from getNext(), but L2R was already killed/null.", q.getName());
}
} else {
logger.warn("Queue from getNext() was null, something is now WRONG.");
}
} while (r == null);
return r;
}
public void complete(Queue queue) {
long s = queue.getSize();
logger.trace("[{}] Drain complete, Queue has {} elements.", queue.getName(), s);
if (s > 0) {
add(queue);
}
}
public void remove(Queue item) {
workList.remove(item);
}
public void add(Queue item) {
logger.trace("[{}] Attempting to add Queue to workList.", item.getName());
if (workList.add(item)) {
logger.trace("[{}] Added Queue to workList.", item.getName());
} else {
logger.trace("[{}] No need to add Queue, already present.", item.getName());
}
}
// QueueListener
public void onNotEmpty(Queue queue) {
logger.trace("[{}] Called onNotEmpty for Queue id={}", queue.getName(), queue.getId());
add(queue);
}
// BlockingQueue. Need to support take(), drainTo() and size() for a ThreadPoolExecutor.
public Runnable take() throws InterruptedException {
logger.trace("take()");
try {
return getNextRemoteTransfer();
} finally {
scheduledCount.incrementAndGet();
}
}
public int drainTo(Collection<? super Runnable> c) {
logger.trace("drainTo(Collection)");
int i = 0;
List<Queue> qs = new ArrayList<Queue>();
workList.drainTo(qs);
for (Queue q:qs) {
Runnable r = new RemotingQueueDrain(q, processors.get(q), maxConcurrentRequests, this);
c.add(r);
i++;
}
return i;
}
public int size() {
logger.trace("size()");
return workList.size();
}
}