/*
* Copyright 2014, The Sporting Exchange Limited
*
* 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 com.betfair.cougar.transport.jms;
import com.betfair.cougar.api.ExecutionContext;
import com.betfair.cougar.api.RequestUUID;
import com.betfair.cougar.api.export.Protocol;
import com.betfair.cougar.api.geolocation.GeoLocationDetails;
import com.betfair.cougar.api.security.Credential;
import com.betfair.cougar.api.security.Identity;
import com.betfair.cougar.api.security.IdentityChain;
import com.betfair.cougar.core.api.ServiceVersion;
import com.betfair.cougar.core.api.ev.ExecutionObserver;
import com.betfair.cougar.core.api.ev.ExecutionResult;
import com.betfair.cougar.core.api.ev.Subscription;
import com.betfair.cougar.core.api.events.Event;
import com.betfair.cougar.core.api.events.EventTransportIdentity;
import com.betfair.cougar.core.api.exception.CougarException;
import com.betfair.cougar.core.api.exception.CougarFrameworkException;
import com.betfair.cougar.core.api.exception.CougarValidationException;
import com.betfair.cougar.core.api.transports.EventTransportMode;
import org.slf4j.LoggerFactory;
import com.betfair.cougar.transport.api.protocol.events.EventBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.events.EventErrorHandler;
import com.betfair.cougar.transport.api.protocol.events.EventServiceBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.events.jms.JMSDestinationResolver;
import com.betfair.cougar.transport.jms.monitoring.ConnectionMonitor;
import com.betfair.cougar.transport.jms.monitoring.TopicSubscriberPingMonitor;
import com.betfair.tornjak.monitor.Monitor;
import com.betfair.tornjak.monitor.MonitorRegistry;
import junit.framework.Assert;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.mockito.Mockito;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static junit.framework.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
/**
* Test case for SonicEventTransportImpl
*
*/
public class JmsEventTransportImplTest {
private JmsEventTransportImpl cut;
private EventServiceBindingDescriptor evsd;
private Object observedResult;
private Subscription observedSubscription;
private Session mockedSession;
private MessageListener messageListener;
private MessageConsumer mockedConsumer;
private MonitorRegistry monitorRegistry;
private Connection mockedConnection;
private ExecutionObserver inbredExecutionObserver = new ExecutionObserver() {
@Override
public void onResult(ExecutionResult result) {
switch (result.getResultType()) {
case Success:
observedResult = result.getResult();
break;
case Fault:
observedResult = result.getFault();
break;
case Subscription:
observedResult = result.getSubscription();
break;
}
}
};
private static final String EVENT_NAME = "myDummyEvent";
@Before
public void setup() throws Exception {
mockedConsumer = Mockito.mock(TopicSubscriber.class);
mockedSession = Mockito.mock(Session.class);
when(mockedSession.createConsumer((Destination)anyObject())).thenReturn(mockedConsumer);
MessageProducer mockedProducer = Mockito.mock(MessageProducer.class);
when(mockedSession.createProducer((Destination)anyObject())).thenReturn(mockedProducer);
TopicSubscriber mockedTopicSubscriber = Mockito.mock(TopicSubscriber.class);
when(mockedSession.createDurableSubscriber((Topic)anyObject(), anyString())).thenReturn(mockedTopicSubscriber);
mockedConnection = Mockito.mock(Connection.class);
ConnectionFactory mockedConnectionFactory = Mockito.mock(ConnectionFactory.class);
when(mockedConnectionFactory.createConnection(anyString(), anyString())).thenReturn(mockedConnection);
cut = new JmsEventTransportImpl(mockedConnectionFactory);
cut.setDestinationResolver(new EventNameDestinationResolver("base"));
cut.setConnectionMonitor(new ConnectionMonitor(false));
monitorRegistry = mock(MonitorRegistry.class);
cut.setMonitorRegistry(monitorRegistry);
TopicSubscriberPingMonitor pingMonitor = mock(TopicSubscriberPingMonitor.class);
messageListener = cut.new SubscriptionMessageListener(inbredExecutionObserver, DummyEventImpl.class, pingMonitor);
when(mockedConsumer.getMessageListener()).thenReturn(messageListener);
JMSEventUnMarshaller unMarshaller = Mockito.mock(JMSEventUnMarshaller.class);
when(unMarshaller.unmarshallEvent(argThat(contains(DummyEventImpl.class)), eq(DummyEventImpl.class), (TextMessage) anyObject())).thenReturn(DummyEventImpl.BOBS_ADDRESS);
cut.setEventUnMarshaller(unMarshaller);
TextMessage mockedMessage = Mockito.mock(TextMessage.class);
JMSEventMarshaller marshaller = Mockito.mock(JMSEventMarshaller.class);
when(marshaller.marshallEvent((EventServiceBindingDescriptor) Matchers.anyObject(), (Event)anyObject(), (Session)anyObject())).thenReturn(mockedMessage);
cut.setEventMarshaller(marshaller);
evsd = new EventServiceBindingDescriptor() {
@Override
public EventBindingDescriptor[] getEventBindings() {
return new EventBindingDescriptor[] {
new EventBindingDescriptor() {
@Override
public String getEventName() {
return EVENT_NAME;
}
@Override
public Class<? extends Event> getEventClass() {
return DummyEventImpl.class;
}
}
};
}
@Override
public String getServiceName() {
return "TestService";
}
@Override
public String getServiceNamespace() {
return "ns";
}
@Override
public ServiceVersion getServiceVersion() {
return new ServiceVersion("v1.0");
}
@Override
public Protocol getServiceProtocol() {
return Protocol.JMS;
}
};
}
private void setupDefaultMockedConnectionBehaviour() throws JMSException {
when(mockedConnection.createSession(anyBoolean(), anyInt())).thenReturn(mockedSession);
}
private Matcher<List<Class<? extends Event>>> contains(final Class<DummyEventImpl> dummyEventClass) {
return new ArgumentMatcher<List<Class<? extends Event>>>() {
@Override
public boolean matches(Object o) {
if (o instanceof List) {
List list = (List) o;
return list.contains(dummyEventClass);
}
return false;
}
};
}
@Test
public void testSubscribe() throws JMSException {
setupDefaultMockedConnectionBehaviour();
//try subscribing to an event that doesn't exist
try {
cut.subscribe("bobTheBuilder", null, null);
fail("Should have thrown an exception here");
} catch (CougarValidationException anticipated) {}
cut.notify(evsd, EventTransportMode.Subscribe);
try {
cut.subscribe("bobTheBuilder", null, null);
fail("Should have thrown an exception here");
} catch (CougarValidationException anticipated) {}
//Check that a subscription identifier name is expected
cut.setDestinationType(JmsEventTransportImpl.DestinationType.DurableTopic);
cut.subscribe(EVENT_NAME, null, inbredExecutionObserver);
assertEquals("Should have thrown a CougarFrameworkException as a subscription id must be supplied", CougarFrameworkException.class, this.observedResult.getClass());
observedResult = null;
cut.subscribe(EVENT_NAME, new Object[] { "myConnectionId" }, inbredExecutionObserver);
assertNotNull(observedResult);
assertTrue(observedResult instanceof Subscription);
ArgumentCaptor<Monitor> monitorCaptor = ArgumentCaptor.forClass(Monitor.class);
verify(monitorRegistry, times(2)).addMonitor(monitorCaptor.capture());
Assert.assertTrue(containsInstanceOf(monitorCaptor.getAllValues(), TopicSubscriberPingMonitor.class));
Assert.assertTrue(containsInstanceOf(monitorCaptor.getAllValues(), ConnectionMonitor.class));
TextMessage mockedMessage = Mockito.mock(TextMessage.class);
mockedMessage.setText("{\"name\":\"bob\", \"address\":\"100 chancellors\"}");
messageListener.onMessage(mockedMessage);
assertNotNull(observedResult);
}
private boolean containsInstanceOf(List<Monitor> allValues, Class c) {
for (Monitor m : allValues) {
if (c.isAssignableFrom(m.getClass())) {
return true;
}
}
return false;
}
@Test
public void testPublish() throws JMSException {
setupDefaultMockedConnectionBehaviour();
cut.setDestinationType(JmsEventTransportImpl.DestinationType.Queue);
cut.initThreadPool();
cut.notify(evsd);
cut.publish(DummyEventImpl.BOBS_ADDRESS);
//check exception wrapping mechanism for when a JMS exception is thrown
JMSDestinationResolver alwaysResolvesNull = new JMSDestinationResolver<String>() {
@Override
public String resolveDestination(Class<? extends Event> eventClass, Object[] args) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
};
// Test exceptions thrown at publication time are passed out correctly
cut.setDestinationResolver(alwaysResolvesNull);
when(mockedSession.createProducer(null)).thenThrow(new JMSException("xx"));
try {
cut.publish(DummyEventImpl.BOBS_ADDRESS);
fail("An exception should have been thrown here");
} catch (CougarException ignored) {}
}
@Test
// Test exceptions thrown when session created are passed out correctly
public void testSessionCreationFailure() throws JMSException {
cut.setDestinationType(JmsEventTransportImpl.DestinationType.Queue);
cut.initThreadPool();
cut.notify(evsd);
when(mockedConnection.createSession(anyBoolean(),anyInt())).thenThrow(new JMSException("xx"));
try {
cut.publish(DummyEventImpl.BOBS_ADDRESS);
fail("An exception should have been thrown here");
} catch (CougarException ignored) {}
}
@Test
public void testOnMessage() throws JMSException {
TopicSubscriberPingMonitor pingMonitor = mock(TopicSubscriberPingMonitor.class);
JmsEventTransportImpl.SubscriptionMessageListener sml=new JmsEventTransportImpl(null).new SubscriptionMessageListener(null,null, pingMonitor);
sml.onMessage(null);
}
@Test
public void testOnMessage2() throws JMSException {
TopicSubscriberPingMonitor pingMonitor = mock(TopicSubscriberPingMonitor.class);
JmsEventTransportImpl.SubscriptionMessageListener sml=new JmsEventTransportImpl(null).new SubscriptionMessageListener(faultyExecutionObserver,null, pingMonitor);
TextMessage mockedMessage = Mockito.mock(TextMessage.class);
sml.onMessage(mockedMessage);
}
@Test
public void testOnMessage3() throws JMSException {
cut.setErrorHandler(faultyErrorHandler);
TopicSubscriberPingMonitor pingMonitor = mock(TopicSubscriberPingMonitor.class);
JmsEventTransportImpl.SubscriptionMessageListener sml=cut.new SubscriptionMessageListener(faultyExecutionObserver,null, pingMonitor);
TextMessage mockedMessage = Mockito.mock(TextMessage.class);
sml.onMessage(mockedMessage);
}
@Test
public void testExecutionContextCreation1() {
cut.initialiseExecutionContext();
assertNotNull(cut.getExecutionContext());
//Start with an empty execution context
List<EventTransportIdentity> transportIdentityChain = cut.getExecutionContext().getIdentity().getIdentities(EventTransportIdentity.class);
assertNotNull(transportIdentityChain);
assertFalse(transportIdentityChain.isEmpty());
EventTransportIdentity eti = transportIdentityChain.get(0);
assertEquals(eti.getEventTransportName(), "JmsEventTransportImpl");
}
@Test
public void testExecutionContextCreation2() {
final List<Identity> identities = new ArrayList<Identity>();
identities.add(new Identity() {
@Override
public Principal getPrincipal() {
return new Principal() {
@Override
public String getName() {
return "TestPrincipal";
}
};
}
public Credential getCredential() { return null; }
});
final IdentityChain identityChain = new IdentityChain() {
@Override
public List<Identity> getIdentities() {
return identities;
}
@Override
public void addIdentity(Identity identity) {
identities.add(identity);
}
@Override
public <T extends Identity> List<T> getIdentities(Class<T> clazz) {
List<T> identityList = new ArrayList<T>();
for (Identity i : identities) {
if (clazz.isAssignableFrom(i.getClass())) {
identityList.add((T)i);
}
}
return identityList;
}
};
cut.setExecutionContext(new ExecutionContext() {
public IdentityChain getIdentity() { return identityChain; }
public GeoLocationDetails getLocation() { return null; }
public RequestUUID getRequestUUID() { return null; }
public Date getReceivedTime() { return null; }
public Date getRequestTime() { return null; }
public boolean traceLoggingEnabled() { return false; }
public int getTransportSecurityStrengthFactor() { return 0; }
public boolean isTransportSecure() { return false; }
});
cut.initialiseExecutionContext();
assertNotNull(cut.getExecutionContext());
assertTrue(cut.getExecutionContext().getIdentity().getIdentities().size() == 2);
List<EventTransportIdentity> transportIdentityList =
cut.getExecutionContext().getIdentity().getIdentities(EventTransportIdentity.class);
assertNotNull(transportIdentityList);
assertFalse(transportIdentityList.isEmpty());
EventTransportIdentity eti = transportIdentityList.get(0);
assertEquals(eti.getEventTransportName(), "JmsEventTransportImpl");
}
private ExecutionObserver faultyExecutionObserver = new ExecutionObserver() {
@Override
public void onResult(ExecutionResult executionResult) {
throw new RuntimeException(executionResult.getResultType() + "-told you it was faulty.");
}
};
private EventErrorHandler faultyErrorHandler=new EventErrorHandler() {
@Override
public void handleEventProcessingError(Object errorEvent, Throwable exception) {
throw new RuntimeException(" handleEventProcessingError-Told you it was faulty.");
}
} ;
}