package rocks.inspectit.agent.java.connection.impl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.slf4j.Logger;
import org.testng.annotations.Test;
import com.esotericsoftware.kryonet.rmi.TimeoutException;
import rocks.inspectit.agent.java.connection.RetryStrategy;
import rocks.inspectit.agent.java.connection.ServerUnavailableException;
import rocks.inspectit.shared.all.cmr.service.IAgentService;
import rocks.inspectit.shared.all.cmr.service.IAgentStorageService;
import rocks.inspectit.shared.all.cmr.service.IKeepAliveService;
import rocks.inspectit.shared.all.communication.DefaultData;
import rocks.inspectit.shared.all.communication.data.TimerData;
import rocks.inspectit.shared.all.communication.message.IAgentMessage;
import rocks.inspectit.shared.all.exception.BusinessException;
import rocks.inspectit.shared.all.instrumentation.classcache.Type;
import rocks.inspectit.shared.all.instrumentation.config.impl.AgentConfig;
import rocks.inspectit.shared.all.instrumentation.config.impl.InstrumentationDefinition;
import rocks.inspectit.shared.all.instrumentation.config.impl.JmxAttributeDescriptor;
import rocks.inspectit.shared.all.kryonet.Client;
import rocks.inspectit.shared.all.testbase.TestBase;
@SuppressWarnings({ "PMD", "unchecked" })
public class KryoNetConnectionTest extends TestBase {
@InjectMocks
KryoNetConnection connection;
@Mock
Logger log;
@Mock
Client client;
@Mock
IAgentStorageService agentStorageService;
@Mock
IAgentService agentService;
@Mock
IKeepAliveService keepAliveService;
public static class Connect extends KryoNetConnectionTest {
@Test
public void connect() throws IOException {
when(client.isConnected()).thenReturn(false);
String host = "host";
int port = 22;
connection.connect(host, port);
verify(client).start();
verify(client).connect(anyInt(), eq(host), eq(port));
verify(client, atLeast(1)).isConnected();
verifyNoMoreInteractions(client);
}
@Test
public void alreadyConnected() throws ConnectException {
when(client.isConnected()).thenReturn(true);
connection.connect("host", 22);
verify(client).isConnected();
verifyNoMoreInteractions(client);
}
@Test(expectedExceptions = ConnectException.class)
public void ioException() throws IOException {
when(client.isConnected()).thenReturn(false);
String host = "host";
int port = 22;
doThrow(IOException.class).when(client).connect(anyInt(), eq(host), eq(port));
connection.connect(host, port);
}
}
public static class Reconnect extends KryoNetConnectionTest {
@Test
public void reconnect() throws IOException {
when(client.isConnected()).thenReturn(false);
connection.reconnect();
verify(client).reconnect();
verify(client, atLeast(1)).isConnected();
verifyNoMoreInteractions(client);
}
@Test
public void alreadyConnected() throws ConnectException {
when(client.isConnected()).thenReturn(true);
connection.reconnect();
verify(client).isConnected();
verifyNoMoreInteractions(client);
}
@Test(expectedExceptions = ConnectException.class)
public void ioException() throws IOException {
when(client.isConnected()).thenReturn(false);
doThrow(IOException.class).when(client).reconnect();
connection.reconnect();
}
}
public static class Disconnect extends KryoNetConnectionTest {
@Test
public void disconnect() {
connection.disconnect();
verify(client).stop();
}
}
public static class KeepAlive extends KryoNetConnectionTest {
@Test
public void keepAlive() throws Exception {
when(client.isConnected()).thenReturn(true);
long id = 3L;
connection.sendKeepAlive(id);
verify(keepAliveService, times(1)).sendKeepAlive(id);
verifyNoMoreInteractions(keepAliveService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(keepAliveService).sendKeepAlive(anyLong());
long id = 3L;
try {
connection.sendKeepAlive(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(keepAliveService, times(1)).sendKeepAlive(id);
verifyNoMoreInteractions(keepAliveService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(keepAliveService).sendKeepAlive(anyLong());
long id = 3L;
try {
connection.sendKeepAlive(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(keepAliveService, times(1)).sendKeepAlive(id);
verifyNoMoreInteractions(keepAliveService);
verify(client).close();
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
long id = 3L;
try {
connection.sendKeepAlive(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(keepAliveService);
}
}
}
public static class SendData extends KryoNetConnectionTest {
@Test
public void sendData() throws Exception {
when(client.isConnected()).thenReturn(true);
List<DefaultData> measurements = new ArrayList<DefaultData>();
measurements.add(new TimerData());
connection.sendDataObjects(measurements);
verify(agentStorageService, times(1)).addDataObjects(measurements);
verifyNoMoreInteractions(agentStorageService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentStorageService).addDataObjects(Matchers.<List<? extends DefaultData>> any());
List<DefaultData> measurements = new ArrayList<DefaultData>();
measurements.add(new TimerData());
try {
connection.sendDataObjects(measurements);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentStorageService, times(1)).addDataObjects(measurements);
verifyNoMoreInteractions(agentStorageService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentStorageService).addDataObjects(Matchers.<List<? extends DefaultData>> any());
List<DefaultData> measurements = new ArrayList<DefaultData>();
measurements.add(new TimerData());
try {
connection.sendDataObjects(measurements);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// call depends on the retry strategy
verify(agentStorageService, times(RetryStrategy.DEFAULT_NUMBER_OF_RETRIES)).addDataObjects(measurements);
verifyNoMoreInteractions(agentStorageService);
verify(client).close();
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
List<DefaultData> measurements = new ArrayList<DefaultData>();
measurements.add(new TimerData());
try {
connection.sendDataObjects(measurements);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentStorageService);
}
}
}
public static class Register extends KryoNetConnectionTest {
@Test
public void register() throws Exception {
AgentConfig agentConfiguration = mock(AgentConfig.class);
when(client.isConnected()).thenReturn(true);
doReturn(agentConfiguration).when(agentService).register(Matchers.<List<String>> any(), anyString(), anyString());
String agentName = "agentName";
String version = "version";
AgentConfig receivedAgentConfiguration = connection.register(agentName, version);
assertThat(receivedAgentConfiguration, is(agentConfiguration));
verify(agentService, times(1)).register(Matchers.<List<String>> any(), eq(agentName), eq(version));
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentService).register(Matchers.<List<String>> any(), anyString(), anyString());
String agentName = "agentName";
String version = "version";
try {
connection.register(agentName, version);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService, times(1)).register(Matchers.<List<String>> any(), eq(agentName), eq(version));
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentService).register(Matchers.<List<String>> any(), anyString(), anyString());
String agentName = "agentName";
String version = "version";
try {
connection.register(agentName, version);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(agentService, times(1)).register(Matchers.<List<String>> any(), eq(agentName), eq(version));
verifyNoMoreInteractions(agentService);
verify(client).close();
}
}
@Test(expectedExceptions = { BusinessException.class })
public void businessException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(BusinessException.class).when(agentService).register(Matchers.<List<String>> any(), anyString(), anyString());
String agentName = "agentName";
String version = "version";
try {
connection.register(agentName, version);
} finally {
verify(agentService, times(1)).register(Matchers.<List<String>> any(), eq(agentName), eq(version));
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
String agentName = "agentName";
String version = "version";
try {
connection.register(agentName, version);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
public static class Unregister extends KryoNetConnectionTest {
@Test
public void unregister() throws Exception {
when(client.isConnected()).thenReturn(true);
long platformId = 10L;
connection.unregister(platformId);
verify(agentService, times(1)).unregister(platformId);
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentService).unregister(anyLong());
long platformId = 10L;
try {
connection.unregister(platformId);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService, times(1)).unregister(platformId);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentService).unregister(anyLong());
long platformId = 10L;
try {
connection.unregister(platformId);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(agentService, times(1)).unregister(platformId);
verifyNoMoreInteractions(agentService);
verify(client).close();
}
}
@Test(expectedExceptions = { BusinessException.class })
public void businessException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(BusinessException.class).when(agentService).unregister(anyLong());
long platformId = 10L;
try {
connection.unregister(platformId);
} finally {
verify(agentService, times(1)).unregister(platformId);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
long platformId = 10L;
try {
connection.unregister(platformId);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
public static class Analyze extends KryoNetConnectionTest {
@Test
public void analyzeAndInstrument() throws Exception {
InstrumentationDefinition instrumentationResult = mock(InstrumentationDefinition.class);
when(client.isConnected()).thenReturn(true);
doReturn(instrumentationResult).when(agentService).analyze(anyLong(), anyString(), Matchers.<Type> any());
long id = 7;
String hash = "hash";
Type type = mock(Type.class);
InstrumentationDefinition receivedResult = connection.analyze(id, hash, type);
assertThat(receivedResult, is(instrumentationResult));
verify(agentService, times(1)).analyze(id, hash, type);
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentService).analyze(anyLong(), anyString(), Matchers.<Type> any());
long id = 7;
String hash = "hash";
Type type = mock(Type.class);
try {
connection.analyze(id, hash, type);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService, times(1)).analyze(id, hash, type);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentService).analyze(anyLong(), anyString(), Matchers.<Type> any());
long id = 7;
String hash = "hash";
Type type = mock(Type.class);
try {
connection.analyze(id, hash, type);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(agentService, times(1)).analyze(id, hash, type);
verifyNoMoreInteractions(agentService);
verify(client).close();
}
}
@Test(expectedExceptions = { BusinessException.class })
public void businessException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(BusinessException.class).when(agentService).analyze(anyLong(), anyString(), Matchers.<Type> any());
long id = 7;
String hash = "hash";
Type type = mock(Type.class);
try {
connection.analyze(id, hash, type);
} finally {
verify(agentService, times(1)).analyze(id, hash, type);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
long id = 7;
String hash = "hash";
Type type = mock(Type.class);
try {
connection.analyze(id, hash, type);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
public static class AnalyzeJmxAttributes extends KryoNetConnectionTest {
@Test
public void analyzeJmxAttributes() throws Exception {
Collection<JmxAttributeDescriptor> result = mock(Collection.class);
when(client.isConnected()).thenReturn(true);
doReturn(result).when(agentService).analyzeJmxAttributes(anyLong(), Matchers.<Collection<JmxAttributeDescriptor>> any());
long id = 7;
Collection<JmxAttributeDescriptor> descriptors = Collections.emptyList();
Collection<JmxAttributeDescriptor> receivedResult = connection.analyzeJmxAttributes(id, descriptors);
assertThat(receivedResult, is(result));
verify(agentService, times(1)).analyzeJmxAttributes(id, descriptors);
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentService).analyzeJmxAttributes(anyLong(), Matchers.<Collection<JmxAttributeDescriptor>> any());
long id = 7;
Collection<JmxAttributeDescriptor> descriptors = Collections.emptyList();
try {
connection.analyzeJmxAttributes(id, descriptors);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService, times(1)).analyzeJmxAttributes(id, descriptors);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentService).analyzeJmxAttributes(anyLong(), Matchers.<Collection<JmxAttributeDescriptor>> any());
long id = 7;
Collection<JmxAttributeDescriptor> descriptors = Collections.emptyList();
try {
connection.analyzeJmxAttributes(id, descriptors);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(agentService, times(1)).analyzeJmxAttributes(id, descriptors);
verifyNoMoreInteractions(agentService);
verify(client).close();
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
long id = 7;
Collection<JmxAttributeDescriptor> descriptors = Collections.emptyList();
try {
connection.analyzeJmxAttributes(id, descriptors);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
public static class InstrumentationApplied extends KryoNetConnectionTest {
@Test
public void instrumentationApplied() throws Exception {
when(client.isConnected()).thenReturn(true);
Map<Long, long[]> methodToSensorMap = mock(Map.class);
when(methodToSensorMap.isEmpty()).thenReturn(false);
long id = 7;
connection.instrumentationApplied(id, methodToSensorMap);
verify(agentService, times(1)).instrumentationApplied(id, methodToSensorMap);
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(TimeoutException.class).when(agentService).instrumentationApplied(anyLong(), Matchers.<Map<Long, long[]>> any());
Map<Long, long[]> methodToSensorMap = mock(Map.class);
when(methodToSensorMap.isEmpty()).thenReturn(false);
long id = 7;
try {
connection.instrumentationApplied(id, methodToSensorMap);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService, times(1)).instrumentationApplied(id, methodToSensorMap);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
doThrow(RuntimeException.class).when(agentService).instrumentationApplied(anyLong(), Matchers.<Map<Long, long[]>> any());
Map<Long, long[]> methodToSensorMap = mock(Map.class);
when(methodToSensorMap.isEmpty()).thenReturn(false);
long id = 7;
try {
connection.instrumentationApplied(id, methodToSensorMap);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// call depends on the retry strategy
verify(agentService, times(RetryStrategy.DEFAULT_NUMBER_OF_RETRIES)).instrumentationApplied(id, methodToSensorMap);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
Map<Long, long[]> methodToSensorMap = mock(Map.class);
long id = 7;
try {
connection.instrumentationApplied(id, methodToSensorMap);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
/**
* Tests the {@link KryoNetConnection#fetchAgentMessages(long)} method.
*/
public static class FetchAgentMessages extends KryoNetConnectionTest {
@Test
public void fetchAgentMessages() throws Exception {
IAgentMessage<?> message = mock(IAgentMessage.class);
List<IAgentMessage<?>> messages = Arrays.<IAgentMessage<?>> asList(message);
when(client.isConnected()).thenReturn(true);
long id = 7;
when(agentService.fetchAgentMessages(id)).thenReturn(messages);
List<IAgentMessage<?>> result = connection.fetchAgentMessages(id);
assertThat(result, is(equalTo(messages)));
verify(agentService).fetchAgentMessages(id);
verifyNoMoreInteractions(agentService);
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void timeout() throws Exception {
when(client.isConnected()).thenReturn(true);
long id = 7;
when(agentService.fetchAgentMessages(id)).thenThrow(TimeoutException.class);
try {
connection.fetchAgentMessages(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(true));
throw e;
} finally {
verify(agentService).fetchAgentMessages(id);
verifyNoMoreInteractions(agentService);
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void remoteException() throws Exception {
when(client.isConnected()).thenReturn(true);
long id = 7;
when(agentService.fetchAgentMessages(id)).thenThrow(RuntimeException.class);
try {
connection.fetchAgentMessages(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
// fail fast call, only one attempt
verify(agentService).fetchAgentMessages(id);
verifyNoMoreInteractions(agentService);
verify(client).close();
}
}
@Test(expectedExceptions = { ServerUnavailableException.class })
public void notConnected() throws Exception {
when(client.isConnected()).thenReturn(false);
long id = 7;
try {
connection.fetchAgentMessages(id);
} catch (ServerUnavailableException e) {
assertThat(e.isServerTimeout(), is(false));
throw e;
} finally {
verifyZeroInteractions(agentService);
}
}
}
}