/*
* JBoss, Home of Professional Open Source
* Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.messaging.core.server.impl;
import org.jboss.messaging.core.message.impl.MessageImpl;
import org.jboss.messaging.core.server.Consumer;
import org.jboss.messaging.core.server.ServerMessage;
import org.jboss.messaging.core.server.HandleStatus;
import org.jboss.messaging.core.server.MessageReference;
import org.jboss.messaging.util.SimpleString;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Distributes message based on the message property 'JMSXGroupID'. Once a message has been successfully delivered to a
* consumer that consumer is then bound to that group. Any message that has the same group id set will always be
* delivered to the same consumer.
* The Initial consumer is the first consumer found, using the round robin policy, that hasn't been bound to a group, If
* there are no consumers left that have not been bound to a group then the next consumer will be bound to 2 groups and
* so on.
*
* @author <a href="mailto:andy.taylor@jboss.org">Andy Taylor</a>
*/
public class GroupingRoundRobinDistributionPolicy extends RoundRobinDistributionPolicy
{
// Constants -----------------------------------------------------
// Static --------------------------------------------------------
// Attributes ----------------------------------------------------
private ConcurrentHashMap<SimpleString, Consumer> cons = new ConcurrentHashMap<SimpleString, Consumer>();
// Distributor implementation ------------------------------------
public HandleStatus distribute(MessageReference reference)
{
final SimpleString groupId = (SimpleString) reference.getMessage().getProperty(MessageImpl.GROUP_ID);
if (groupId != null)
{
boolean bound;
int startPos = pos;
boolean filterRejected = false;
while (true)
{
Consumer consumer = cons.putIfAbsent(groupId, consumers.get(pos));
if (consumer == null)
{
incrementPosition();
consumer = cons.get(groupId);
bound = false;
}
else
{
bound = true;
}
HandleStatus status = handle(reference, consumer);
if (status == HandleStatus.HANDLED)
{
return HandleStatus.HANDLED;
}
else if (status == HandleStatus.NO_MATCH)
{
filterRejected = true;
}
else if (status == HandleStatus.BUSY)
{
//if we were previously bound, we can remove and try the next consumer
if (bound)
{
return HandleStatus.BUSY;
}
else
{
cons.remove(groupId);
}
}
//if we've tried all of them
if (startPos == pos)
{
// Tried all of them
if (filterRejected)
{
return HandleStatus.NO_MATCH;
}
else
{
// Give up - all consumers busy
return HandleStatus.BUSY;
}
}
}
}
else
{
return super.distribute(reference);
}
}
public synchronized boolean removeConsumer(Consumer consumer)
{
boolean removed = super.removeConsumer(consumer);
if (removed)
{
for (SimpleString group : cons.keySet())
{
if (consumer == cons.get(group))
{
cons.remove(group);
break;
}
}
}
return removed;
}
}