package rocks.inspectit.server.property;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
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.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.xml.bind.JAXBException;
import org.apache.commons.collections.MapUtils;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanInitializationException;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;
import rocks.inspectit.server.util.ShutdownService;
import rocks.inspectit.shared.all.testbase.TestBase;
import rocks.inspectit.shared.cs.cmr.property.configuration.AbstractProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.Configuration;
import rocks.inspectit.shared.cs.cmr.property.configuration.PropertySection;
import rocks.inspectit.shared.cs.cmr.property.configuration.SingleProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.LongProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.impl.StringProperty;
import rocks.inspectit.shared.cs.cmr.property.configuration.validation.PropertyValidation;
import rocks.inspectit.shared.cs.cmr.property.update.AbstractPropertyUpdate;
import rocks.inspectit.shared.cs.cmr.property.update.IPropertyUpdate;
import rocks.inspectit.shared.cs.cmr.property.update.configuration.ConfigurationUpdate;
import rocks.inspectit.shared.cs.jaxb.JAXBTransformator;
@SuppressWarnings("PMD")
public class PropertyManagerTest extends TestBase {
@InjectMocks
PropertyManager propertyManager;
@Mock
ConfigurationUpdate configurationUpdate;
@Mock
Configuration configuration;
@Mock
PropertyUpdateExecutor propertyUpdateExecutor;
@Mock
ShutdownService shutdownService;
@Mock
JAXBTransformator transformator;
public class LoadConfigurationAndUpdates extends PropertyManagerTest {
/**
* Tests that the loading of default configuration and updates can be executed with no
* exceptions.
*/
@Test
public void loadDefaultConfiguration() throws JAXBException, IOException, SAXException {
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
}
@Test(expectedExceptions = IOException.class)
public void emptyConfigDir() throws JAXBException, IOException, SAXException {
Path emptyConfigDir = Paths.get("config");
if (!Files.exists(emptyConfigDir)) {
Files.createDirectories(emptyConfigDir);
}
try {
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
} finally {
Files.deleteIfExists(emptyConfigDir);
}
}
}
public class GetProperties extends PropertyManagerTest {
/**
* Test that the {@link Properties} returned by the {@link PropertyManager} are correctly
* take from {@link Configuration}.
*/
@Test
public void propertyInDefaultConfiguration() throws JAXBException, IOException, SAXException {
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
when(configuration.validate()).thenReturn(Collections.<AbstractProperty, PropertyValidation> emptyMap());
SingleProperty<?> property = mock(SingleProperty.class);
Answer<Object> answer = new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Properties properties = (Properties) invocation.getArguments()[0];
properties.put("property1", "value1");
return null;
}
};
doAnswer(answer).when(property).register(Matchers.<Properties> anyObject());
when(configuration.getAllProperties()).thenReturn(Collections.<AbstractProperty> singleton(property));
Properties properties = propertyManager.getProperties();
assertThat(properties.getProperty("property1"), is(equalTo("value1")));
assertThat(properties.size(), is(1));
}
/**
* Test that if validation fails for the property it won't be included in the
* {@link Properties} returned by {@link PropertyManager}.
*/
@SuppressWarnings("unchecked")
@Test
public void propertyNotValidInDefaultConfiguration() throws JAXBException, IOException, SAXException {
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
SingleProperty<?> property = mock(SingleProperty.class);
PropertyValidation propertyValidation = mock(PropertyValidation.class, Mockito.RETURNS_SMART_NULLS);
when(configuration.getAllProperties()).thenReturn(Collections.<AbstractProperty> singleton(property));
when(configuration.validate()).thenReturn(MapUtils.putAll(new HashMap<AbstractProperty, PropertyValidation>(), new Object[][] { { property, propertyValidation } }));
Properties properties = propertyManager.getProperties();
verify(property, times(0)).register(Matchers.<Properties> anyObject());
assertThat(properties.size(), is(0));
}
/**
* Test that {@link Properties} will hold an updated value of the property if it's included
* in the {@link ConfigurationUpdate}.
*/
@Test
@SuppressWarnings("unchecked")
public void savedPropertyUpdateFromTheDefaultConfiguration() throws JAXBException, IOException, SAXException {
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<String> property = new StringProperty("", "", "property1", "value1", false, false);
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(configurationUpdate).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
AbstractPropertyUpdate<String> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.getUpdateValue()).thenReturn("updatedValue");
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
Properties properties = propertyManager.getProperties();
assertThat(properties.getProperty("property1"), is(equalTo("updatedValue")));
assertThat(properties.size(), is(1));
}
/**
* Test that {@link Properties} will reseted to default if it's included in the
* {@link ConfigurationUpdate}.
*/
@Test
@SuppressWarnings("unchecked")
public void savedPropertyRestoreToDefaultUpdate() throws JAXBException, IOException, SAXException {
String defaultValue = "value1";
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<String> property = new StringProperty("", "", "property1", defaultValue, false, false);
property.setValue("someotherthandefault");
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(configurationUpdate).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
AbstractPropertyUpdate<String> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.isRestoreDefault()).thenReturn(true);
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
Properties properties = propertyManager.getProperties();
assertThat(properties.getProperty("property1"), is(equalTo(defaultValue)));
assertThat(properties.size(), is(1));
}
/**
* Test that not matching property update will not be taken into account.
*/
@Test
@SuppressWarnings("unchecked")
public void savedPropertyUpdateNotValid() throws JAXBException, IOException, SAXException {
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<Long> property = new LongProperty("", "", "property1", 10L, false, false);
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(configurationUpdate).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
AbstractPropertyUpdate<String> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.getUpdateValue()).thenReturn("updatedValue");
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
Properties properties = propertyManager.getProperties();
assertThat(Long.valueOf(properties.getProperty("property1")), is(10L));
assertThat(properties.size(), is(1));
}
@Test(expectedExceptions = BeanInitializationException.class)
public void configurationNotLoaded() throws JAXBException, IOException, SAXException {
doThrow(new IOException()).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
propertyManager.getProperties();
}
@Test(expectedExceptions = BeanInitializationException.class)
public void configurationNull() throws JAXBException, IOException, SAXException {
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
propertyManager.getProperties();
}
}
public class UpdateConfiguration extends PropertyManagerTest {
/**
* Check that Exception will be thrown if the {@link ConfigurationUpdate} has update that
* can not be applied.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test(expectedExceptions = { Exception.class })
public void noUpdateIfCanNotUpdateProperty() throws Exception {
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
SingleProperty singleProperty = mock(SingleProperty.class);
when(singleProperty.canUpdate(propertyUpdate)).thenReturn(false);
when(configuration.forLogicalName(Matchers.<String> anyObject())).thenReturn(singleProperty);
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
propertyManager.updateConfiguration(configurationUpdate, false);
}
/**
* Check that Exception will be thrown if the {@link ConfigurationUpdate} has update for non
* existing property.
*/
@SuppressWarnings("unchecked")
@Test(expectedExceptions = { Exception.class })
public void noUpdateIfPropertyNotFound() throws Exception {
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
when(configuration.forLogicalName(Matchers.<String> anyObject())).thenReturn(null);
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
propertyManager.updateConfiguration(configurationUpdate, false);
}
/**
* Check that property will be correctly updated during runtime.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void runtimePropertyUpdate() throws Exception {
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<Long> property = new LongProperty("", "", "property1", 10L, false, false);
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
doNothing().when(transformator).marshall(Matchers.<Path> anyObject(), any(), anyString());
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.getUpdateValue()).thenReturn(20L);
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
propertyManager.updateConfiguration(configurationUpdate, false);
assertThat(property.getValue(), is(20L));
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(propertyUpdateExecutor, times(1)).executePropertyUpdates(captor.capture());
List<SingleProperty<?>> list = captor.getValue();
assertThat(list, hasSize(1));
assertThat(list, hasItem(property));
// confirm configuration update write
verify(transformator, times(1)).marshall(Matchers.<Path> anyObject(), eq(configurationUpdate), anyString());
verifyZeroInteractions(shutdownService);
}
/**
* Check that property will be correctly updated during runtime and server will be
* restarted.
*/
@SuppressWarnings({ "unchecked" })
@Test
public void runtimePropertyUpdateServerRestart() throws Exception {
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<Long> property = new LongProperty("", "", "property1", 10L, false, true);
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
doNothing().when(transformator).marshall(Matchers.<Path> anyObject(), any(), anyString());
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.getUpdateValue()).thenReturn(20L);
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
propertyManager.updateConfiguration(configurationUpdate, true);
assertThat(property.getValue(), is(20L));
// confirm configuration update write
verify(transformator, times(1)).marshall(Matchers.<Path> anyObject(), eq(configurationUpdate), anyString());
// confirm server service was called
verify(shutdownService).restart();
verifyZeroInteractions(propertyUpdateExecutor);
}
/**
* Check that property will be correctly reseted to default during runtime.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void runtimePropertyResetToDefault() throws Exception {
long defaultValue = 10L;
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<Long> property = new LongProperty("", "", "property1", defaultValue, false, false);
property.setValue(45523L);
section.addProperty(property);
configuration.addSection(section);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(null).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
doNothing().when(transformator).marshall(Matchers.<Path> anyObject(), any(), anyString());
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.isRestoreDefault()).thenReturn(true);
when(configurationUpdate.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdate));
propertyManager.init();
propertyManager.loadConfigurationAndUpdates();
propertyManager.updateConfiguration(configurationUpdate, false);
assertThat(property.getValue(), is(defaultValue));
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(propertyUpdateExecutor, times(1)).executePropertyUpdates(captor.capture());
List<SingleProperty<?>> list = captor.getValue();
assertThat(list, hasSize(1));
assertThat(list, hasItem(property));
// confirm configuration update write
verify(transformator, times(1)).marshall(Matchers.<Path> anyObject(), eq(configurationUpdate), anyString());
verifyZeroInteractions(shutdownService);
}
/**
* Confirm restore to default of already existing property.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void runtimePropertyUpdateOfAlreadyUpdatedProperty() throws Exception {
Configuration configuration = new Configuration();
PropertySection section = new PropertySection();
SingleProperty<Long> property = new LongProperty("", "", "property1", 10L, false, false);
section.addProperty(property);
configuration.addSection(section);
ConfigurationUpdate configurationUpdate = mock(ConfigurationUpdate.class, Mockito.RETURNS_MOCKS);
AbstractPropertyUpdate<Long> propertyUpdate = mock(AbstractPropertyUpdate.class);
when(propertyUpdate.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdate.getUpdateValue()).thenReturn(20L);
Set<IPropertyUpdate<?>> propertyUpdates = Mockito.spy(new HashSet<IPropertyUpdate<?>>());
propertyUpdates.add(propertyUpdate);
when(configurationUpdate.getPropertyUpdates()).thenReturn(propertyUpdates);
doReturn(configuration).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(Configuration.class));
doReturn(configurationUpdate).when(transformator).unmarshall(Matchers.<Path> anyObject(), Matchers.<Path> anyObject(), eq(ConfigurationUpdate.class));
doNothing().when(transformator).marshall(Matchers.<Path> anyObject(), any(), anyString());
ConfigurationUpdate configurationUpdateRuntime = mock(ConfigurationUpdate.class);
AbstractPropertyUpdate<Long> propertyUpdateRuntime = mock(AbstractPropertyUpdate.class);
when(propertyUpdateRuntime.getPropertyLogicalName()).thenReturn("property1");
when(propertyUpdateRuntime.getUpdateValue()).thenReturn(10L); // set back to default
// value
when(configurationUpdateRuntime.getPropertyUpdates()).thenReturn(Collections.<IPropertyUpdate<?>> singleton(propertyUpdateRuntime));
propertyManager.init();
propertyManager.getProperties();
propertyManager.updateConfiguration(configurationUpdateRuntime, false);
// confirm property update and value
assertThat(property.getValue(), is(10L)); // returned to the default value
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(propertyUpdateExecutor, times(1)).executePropertyUpdates(captor.capture());
List<SingleProperty<?>> list = captor.getValue();
assertThat(list, hasSize(1));
assertThat(list, hasItem(property));
// confirm merging of configurations and write
verify(configurationUpdate, times(1)).merge(configurationUpdateRuntime, true);
verify(transformator, times(1)).marshall(Matchers.<Path> anyObject(), eq(configurationUpdate), anyString());
verifyZeroInteractions(shutdownService);
}
}
}