/*
Copyright 2014 Philipp Leitner
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.
*/
package at.ac.tuwien.infosys.jcloudscale.vm.bursting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.naming.NamingException;
import at.ac.tuwien.infosys.jcloudscale.configuration.JCloudScaleConfiguration;
import at.ac.tuwien.infosys.jcloudscale.messaging.IMQWrapper;
import at.ac.tuwien.infosys.jcloudscale.messaging.MessageQueueConfiguration;
import at.ac.tuwien.infosys.jcloudscale.messaging.TimeoutException;
import at.ac.tuwien.infosys.jcloudscale.messaging.objects.MessageObject;
import at.ac.tuwien.infosys.jcloudscale.utility.CancellationToken;
/**
* @author RST
* Aggregated IMQWrapper allows to do the same operations on the set of mq wrappers
* connected to different message queue servers.
*/
public class AggregatedMQWrapper implements IMQWrapper
{
private Map<IMQWrapper, String> wrappers = new HashMap<>();
private Logger log = JCloudScaleConfiguration.getLogger(this);
private ExecutorService threadPool;
AggregatedMQWrapper(AggregatedMessageQueueConfiguration configuration)
throws NamingException, JMSException
{
threadPool = Executors.newCachedThreadPool();
for(MessageQueueConfiguration cfg: configuration.getMqConfigurations())
try
{
wrappers.put(cfg.newWrapper(), cfg.getServerAddress());
}
catch(Exception ex)
{
log.severe("Failed to construct MQWrapper for "+cfg.getServerAddress()+": "+ex);
}
}
private static interface IMQAction
{
public MessageObject action(IMQWrapper wrapper) throws Exception;
}
private class MQActionApplier implements Runnable
{
private IMQAction action;
private IMQWrapper wrapper;
private Exception ex;
private CountDownLatch countDown;
private MessageObject result;
private CancellationToken token;
public MQActionApplier(CountDownLatch countDown, IMQWrapper wrapper, IMQAction action, CancellationToken token)
{
this.countDown = countDown;
this.action = action;
this.wrapper = wrapper;
this.token = token;
}
@Override
public void run()
{
try
{
result = this.action.action(wrapper);
if(result != null)
{
//we have to cancel everything, we got result.
if(token != null)
token.cancel();
}
} catch (Exception e)
{
ex = new Exception("Exception while executing task "+ action +" on wrapper to "+ wrappers.get(wrapper)+e, e);
}
finally
{
countDown.countDown();
}
}
public Exception getException()
{
return ex;
}
public MessageObject getResult()
{
return result;
}
}
private MessageObject forAll(IMQAction action)
{
return forAll(action, null);
}
private MessageObject forAll(IMQAction action, CancellationToken token)
{
CountDownLatch countDown = new CountDownLatch(this.wrappers.size());
List<MQActionApplier> appliers = new ArrayList<>();
// creating appliers
for(IMQWrapper wrapper : this.wrappers.keySet())
appliers.add(new MQActionApplier(countDown, wrapper, action, token));
// running appliers
for(MQActionApplier applier : appliers)
threadPool.execute(applier);
// waiting for completion.
try {
countDown.await();
} catch (InterruptedException e)
{
log.severe("Exception while awaiting for method execution: "+e);
}
// analyzing exceptions:
MessageObject result = null;
for(MQActionApplier applier : appliers)
{
if(applier.getException() != null)
{
log.severe("Exception while executing action: "+applier.getException());
applier.getException().printStackTrace();
}
if(applier.getResult() != null)
result = applier.getResult();
}
return result;
}
@Override
public void disconnect() throws JMSException, NamingException
{
close();
}
@Override
public void close()
{
if(threadPool == null)
return;
threadPool.shutdown();
log.info("Aggregated MQ: shutting down.");
try
{
if(!this.threadPool.awaitTermination(10, TimeUnit.SECONDS))
threadPool.shutdownNow();
} catch (InterruptedException e)
{
e.printStackTrace();
}
for(IMQWrapper wrapper : this.wrappers.keySet())
wrapper.close();
threadPool = null;
this.wrappers = null;
}
@Override
public boolean configurationEquals(MessageQueueConfiguration newConfiguration)
{
return true;//should not matter for us, this method is used on server where we should not run.
}
@Override
public void createQueueProducer(final String destName) throws NamingException,JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.createQueueProducer(destName);
return null;
}
});
}
@Override
public void createTopicProducer(final String destName) throws NamingException,JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.createTopicProducer(destName);
return null;
}
});
}
@Override
public void createQueueConsumer(final String destName) throws NamingException,JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.createQueueConsumer(destName);
return null;
}
});
}
@Override
public void createTopicConsumer(final String destName, final String messageSelector) throws NamingException, JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.createTopicConsumer(destName, messageSelector);
return null;
}
});
}
@Override
public void createTopicConsumer(final String destName) throws NamingException, JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.createTopicConsumer(destName);
return null;
}
});
}
@Override
public void registerListener(final MessageListener listener) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.registerListener(listener);
return null;
}
});
}
@Override
public MessageObject requestResponseToCSHost(MessageObject obj, UUID correlationId, UUID hostId) throws JMSException,TimeoutException
{
return requestResponseToCSHost(obj, correlationId, hostId, null);
}
@Override
public MessageObject requestResponse(MessageObject obj, UUID correlationId)
throws JMSException, TimeoutException
{
return requestResponse(obj, correlationId, null);
}
@Override
public MessageObject requestResponseToCSHost(final MessageObject obj, final UUID correlationId, final UUID hostId, CancellationToken cancelToken) throws JMSException, TimeoutException
{
final CancellationToken token = new CancellationToken(cancelToken);
return forAll(new IMQAction() {
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception
{
return wrapper.requestResponseToCSHost(obj, correlationId, hostId, token);
}
}, token);
}
@Override
public MessageObject requestResponse(final MessageObject obj, final UUID correlationId, CancellationToken cancelToken) throws JMSException, TimeoutException
{
final CancellationToken token = new CancellationToken(cancelToken);
return forAll(new IMQAction() {
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception
{
return wrapper.requestResponse(obj, correlationId, token);
}
}, token);
}
@Override
public void oneway(final MessageObject obj) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.oneway(obj);
return null;
}
});
}
@Override
public void oneway(final MessageObject obj, final UUID correlationId) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.oneway(obj, correlationId);
return null;
}
});
}
@Override
public void onewayToCSHost(final MessageObject obj, final UUID correlationId, final UUID hostId) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.onewayToCSHost(obj, correlationId, hostId);
return null;
}
});
}
@Override
public void respond(final MessageObject obj, final Destination dest, final UUID correlationId) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.respond(obj, dest, correlationId);
return null;
}
});
}
@Override
public void respond(final MessageObject obj, final Destination dest) throws JMSException
{
forAll(new IMQAction(){
@Override
public MessageObject action(IMQWrapper wrapper) throws Exception {
wrapper.respond(obj, dest);
return null;
}
});
}
}