/* * Copyright (C) 2019-2023 Ignite Realtime Foundation. All rights reserved. * * 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 org.jivesoftware.util; import org.jivesoftware.Fixtures; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.auth.AuthProvider; import org.jivesoftware.openfire.auth.DefaultAuthProvider; import org.jivesoftware.openfire.auth.HybridAuthProvider; import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.ldap.LdapAuthProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.xmpp.packet.JID; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.doReturn; @ExtendWith(MockitoExtension.class) public class SystemPropertyTest { @Mock private XMPPServer xmppServer; @Mock private PluginManager pluginManager; @BeforeAll public static void setUpClass() throws Exception { Fixtures.reconfigureOpenfireHome(); Fixtures.disableDatabasePersistence(); } @SuppressWarnings({"deprecation"}) @BeforeEach public void setUp() { XMPPServer.setInstance(xmppServer); } @AfterEach public void tearDown() { Fixtures.clearExistingProperties(); } @Test public void willBuildAStringProperty() { final SystemProperty stringProperty = SystemProperty.Builder.ofType(String.class) .setKey("a-test-string-property") .setDefaultValue("this-is-a-default") .setDynamic(true) .build(); assertThat(stringProperty.getValue(), is("this-is-a-default")); assertThat(stringProperty.getValueAsSaved(), is("this-is-a-default")); stringProperty.setValue("this-is-not-a-default"); assertThat(stringProperty.getValue(), is("this-is-not-a-default")); assertThat(stringProperty.getValueAsSaved(), is("this-is-not-a-default")); } @Test public void willBuildALongProperty() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("a-test-long-property") .setDefaultValue(42L) .setDynamic(true) .build(); assertThat(longProperty.getValue(), is(42L)); assertThat(longProperty.getValueAsSaved(), is("42")); longProperty.setValue(84L); assertThat(longProperty.getValue(), is(84L)); assertThat(longProperty.getValueAsSaved(), is("84")); } @Test public void willBuildADoubleProperty() { final SystemProperty doubleProperty = SystemProperty.Builder.ofType(Double.class) .setKey("a-test-double-property") .setDefaultValue(42.2) .setDynamic(true) .build(); assertThat(doubleProperty.getValue(), is(42.2)); assertThat(doubleProperty.getValueAsSaved(), is("42.2")); doubleProperty.setValue(84.1); assertThat(doubleProperty.getValue(), is(84.1)); assertThat(doubleProperty.getValueAsSaved(), is("84.1")); } @Test public void willBuildADurationProperty() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Duration.class) .setKey("a-test-duration-property") .setDefaultValue(Duration.ofHours(1)) .setChronoUnit(ChronoUnit.MINUTES) .setDynamic(true) .build(); assertThat(longProperty.getValue(), is(Duration.ofHours(1))); assertThat(longProperty.getValueAsSaved(), is("60")); longProperty.setValue(Duration.ofDays(1)); assertThat(longProperty.getValue(), is(Duration.ofDays(1))); assertThat(JiveGlobals.getProperty("a-test-duration-property"), is("1440")); assertThat(longProperty.getValueAsSaved(), is("1440")); } @Test public void willBuildABooleanProperty() { final SystemProperty booleanProperty = SystemProperty.Builder.ofType(Boolean.class) .setKey("a-test-boolean-property") .setDefaultValue(false) .setDynamic(true) .build(); assertThat(booleanProperty.getValue(), is(false)); assertThat(booleanProperty.getValueAsSaved(), is("false")); booleanProperty.setValue(true); assertThat(booleanProperty.getValue(), is(true)); assertThat(booleanProperty.getValueAsSaved(), is("true")); } @Test public void willNotBuildAPropertyWithoutAKey() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(String.class).build()); } @Test public void willNotBuildAPropertyWithoutADefaultValue() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(String.class) .setKey("a-test-property-without-a-default-value") .build() ); } @Test public void willNotBuildAPropertyWithoutADynamicIndicator() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(String.class) .setKey("a-test-property-without-dynamic-set") .setDefaultValue("default value") .build() ); } @Test public void willNotBuildAPropertyForAnUnsupportedClass() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(JavaSpecVersion.class) .setKey("a-property-for-an-unsupported-class") .setDefaultValue(new JavaSpecVersion("1.8")) .setDynamic(true) .build() ); } @Test public void willNotBuildTheSamePropertyTwice() { SystemProperty.Builder.ofType(String.class) .setKey("a-duplicate-property") .setDefaultValue("") .setDynamic(true) .build(); assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Boolean.class) .setKey("a-duplicate-property") .setDefaultValue(false) .setDynamic(true) .build() ); } @Test public void willPreventAValueBeingTooLow() { final SystemProperty property = SystemProperty.Builder.ofType(Long.class) .setKey("this-is-a-constrained-long-key") .setDefaultValue(42L) .setMinValue(0L) .setMaxValue(42L) .setDynamic(true) .build(); property.setValue(-1L); assertThat(property.getValue(), is(42L)); assertThat(property.getValueAsSaved(), is("42")); } @Test public void willPreventAValueBeingTooHigh() { final SystemProperty property = SystemProperty.Builder.ofType(Long.class) .setKey("this-is-another-constrained-long-key") .setDefaultValue(42L) .setMinValue(0L) .setMaxValue(42L) .setDynamic(true) .build(); property.setValue(500L); assertThat(property.getValue(), is(42L)); assertThat(property.getValueAsSaved(), is("42")); } @Test public void willNotBuildADurationPropertyWithoutAChronoUnit() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Duration.class) .setKey("this-will-not-work") .setDefaultValue(Duration.ZERO) .setDynamic(true) .build() ); } @Test public void willNotBuildADurationPropertyWithAnInvalidChronoUnit() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Duration.class) .setKey("this-will-not-work") .setDefaultValue(Duration.ZERO) .setChronoUnit(ChronoUnit.CENTURIES) .setDynamic(true) .build() ); } @Test public void willNotifyListenersOfChanges() { final AtomicReference notifiedValue = new AtomicReference<>(); final SystemProperty property = SystemProperty.Builder.ofType(Duration.class) .setKey("property-notifier") .setDefaultValue(Duration.ZERO) .setChronoUnit(ChronoUnit.SECONDS) .setDynamic(true) .addListener(notifiedValue::set) .build(); property.setValue(Duration.ofMinutes(60)); assertThat(notifiedValue.get(), is(Duration.ofHours(1))); } @Test public void willIndicateDynamicPropertyDoesNotNeedRestarting() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("a-test-dynamic-property") .setDefaultValue(42L) .setDynamic(true) .build(); assertThat(longProperty.isDynamic(), is(true)); assertThat(longProperty.isRestartRequired(), is(false)); longProperty.setValue(84L); assertThat(longProperty.isRestartRequired(), is(false)); } @Test public void willIndicateNonDynamicPropertyNeedsRestarting() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("a-test-non-dynamic-property") .setDefaultValue(42L) .setDynamic(false) .build(); assertThat(longProperty.isDynamic(), is(false)); assertThat(longProperty.isRestartRequired(), is(false)); longProperty.setValue(84L); assertThat(longProperty.isRestartRequired(), is(true)); } @Test public void theDefaultPluginIsOpenfire() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("an-openfire-property") .setDefaultValue(42L) .setDynamic(false) .build(); assertThat(longProperty.getPlugin(), is("Openfire")); } @Test public void thePluginCanBeChanged() { doReturn(pluginManager).when(xmppServer).getPluginManager(); final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("a-plugin-property") .setDefaultValue(42L) .setPlugin("TestPluginName") .setDynamic(false) .build(); assertThat(longProperty.getPlugin(), is("TestPluginName")); } @Test public void aPluginIsRequired() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Long.class) .setKey("a-null-plugin-property") .setDefaultValue(42L) .setPlugin(null) .setDynamic(false) .build() ); } @Test public void willReturnAClass() { final SystemProperty classProperty = SystemProperty.Builder.ofType(Class.class) .setKey("a-class-property") .setDefaultValue(DefaultAuthProvider.class) .setBaseClass(AuthProvider.class) .setDynamic(false) .build(); assertThat(classProperty.getValue(), is(equalTo(DefaultAuthProvider.class))); assertThat(classProperty.getValueAsSaved(), is("org.jivesoftware.openfire.auth.DefaultAuthProvider")); JiveGlobals.setProperty("a-class-property", "org.jivesoftware.openfire.auth.HybridAuthProvider"); assertThat(classProperty.getValue(), is(equalTo(HybridAuthProvider.class))); assertThat(classProperty.getValueAsSaved(), is("org.jivesoftware.openfire.auth.HybridAuthProvider")); classProperty.setValue(LdapAuthProvider.class); assertThat(classProperty.getValue(), is(equalTo(LdapAuthProvider.class))); assertThat(classProperty.getValueAsSaved(), is("org.jivesoftware.openfire.ldap.LdapAuthProvider")); } @Test public void willNotReturnAnotherClass() { final SystemProperty classProperty = SystemProperty.Builder.ofType(Class.class) .setKey("another-subclass-property") .setDefaultValue(DefaultAuthProvider.class) .setBaseClass(AuthProvider.class) .setDynamic(false) .build(); JiveGlobals.setProperty("another-subclass-property", "java.lang.Object"); assertThat(classProperty.getValue(), is(equalTo(DefaultAuthProvider.class))); } @Test public void willNotReturnAMissingClass() { final SystemProperty classProperty = SystemProperty.Builder.ofType(Class.class) .setKey("a-missing-subclass-property") .setDefaultValue(DefaultAuthProvider.class) .setBaseClass(AuthProvider.class) .setDynamic(false) .build(); JiveGlobals.setProperty("a-missing-subclass-property", "this.class.is.missing"); assertThat(classProperty.getValue(), is(equalTo(DefaultAuthProvider.class))); } @Test public void willNotBuildAClassPropertyWithoutABaseClass() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Class.class) .setKey("a-broken-class-property") .setDefaultValue(DefaultAuthProvider.class) .setDynamic(false) .build() ); } @Test public void willNotBuildARegularPropertyWithABaseClass() { assertThrows(IllegalArgumentException.class, () -> SystemProperty.Builder.ofType(Long.class) .setKey("a-broken-long-property") .setDefaultValue(42L) .setBaseClass(java.lang.Long.class) .setDynamic(false) .build() ); } @Test public void shouldEncryptAProperty() { final SystemProperty longProperty = SystemProperty.Builder.ofType(Long.class) .setKey("an-encrypted-property") .setDefaultValue(42L) .setDynamic(false) .setEncrypted(true) .build(); longProperty.setValue(84L); assertThat(JiveGlobals.isPropertyEncrypted("an-encrypted-property"), is(true)); } @Test public void willAllowNullDefaultsForAStringProperty() { final SystemProperty stringProperty = SystemProperty.Builder.ofType(String.class) .setKey("a-null-string-property") .setDynamic(true) .build(); assertThat(stringProperty.getDefaultValue(), is(nullValue())); } @Test public void willAllowNullDefaultsForAClassProperty() { final SystemProperty classProperty = SystemProperty.Builder.ofType(Class.class) .setKey("a-null-class-property") .setBaseClass(AuthProvider.class) .setDynamic(false) .build(); assertThat(classProperty.getDefaultValue(), is(nullValue())); } @Test public void willRemovePluginSpecificProperties() { doReturn(pluginManager).when(xmppServer).getPluginManager(); final String key = "a-class-property-to-remove"; final SystemProperty property = SystemProperty.Builder.ofType(Class.class) .setKey(key) .setBaseClass(AuthProvider.class) .setPlugin("TestPluginName") .setDynamic(false) .build(); assertThat(SystemProperty.getProperty(key), is(Optional.of(property))); SystemProperty.removePropertiesForPlugin("TestPluginName"); assertThat(SystemProperty.getProperty(key), is(Optional.empty())); } @Test public void willCreateAnInstantProperty() { final String key = "test.instant.property"; final SystemProperty property = SystemProperty.Builder.ofType(Instant.class) .setKey(key) .setDynamic(true) .build(); assertThat(property.getValue(), is(nullValue())); final Instant value = Instant.now().truncatedTo(ChronoUnit.MILLIS); property.setValue(value); assertThat(property.getValue(), is(value)); } @Test public void willCreateAnInstantPropertyWithADefaultValue() { final String key = "test.instant.property.with.default"; final Instant defaultValue = Instant.now().truncatedTo(ChronoUnit.MILLIS); final SystemProperty property = SystemProperty.Builder.ofType(Instant.class) .setKey(key) .setDefaultValue(defaultValue) .setDynamic(true) .build(); assertThat(property.getValue(), is(defaultValue)); } @Test public void willCreateAListOfStringProperties() { final String key = "a list property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setKey(key) .setDefaultValue(Collections.emptyList()) .setDynamic(true) .buildList(String.class); assertThat(property.getValue(), is(Collections.emptyList())); property.setValue(Arrays.asList("3", "2", "1")); assertThat(property.getValue(), is(Arrays.asList("3", "2", "1"))); assertThat(property.getDisplayValue(), is("3,2,1")); assertThat(JiveGlobals.getProperty(key), is("3,2,1")); } @Test public void willCreateAListOfLongProperties() { final String key = "a list of longs property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setKey(key) .setSorted(true) .setDefaultValue(Collections.emptyList()) .setDynamic(true) .buildList(Long.class); assertThat(property.getValue(), is(Collections.emptyList())); property.setValue(Arrays.asList(3L, 2L, 1L)); assertThat(property.getValue(), is(Arrays.asList(1L, 2L, 3L))); assertThat(property.getDisplayValue(), is("1,2,3")); assertThat(JiveGlobals.getProperty(key), is("1,2,3")); JiveGlobals.setProperty(key, "3,2,1"); assertThat(property.getValue(), is(Arrays.asList(1L, 2L, 3L))); assertThat(property.getDisplayValue(), is("1,2,3")); } @Test public void willCreateAListOfDurationProperties() { final String key = "a list of durations property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setChronoUnit(ChronoUnit.HOURS) .setKey(key) .setDefaultValue(Collections.singletonList(Duration.ZERO)) .setDynamic(true) .buildList(Duration.class); assertThat(property.getValue(), is(Collections.singletonList(Duration.ZERO))); property.setValue(Arrays.asList(Duration.ofHours(1), Duration.ZERO)); assertThat(property.getValue(), is(Arrays.asList(Duration.ofHours(1), Duration.ZERO))); assertThat(property.getDisplayValue(), is("1 hour,0 ms")); assertThat(JiveGlobals.getProperty(key), is("1,0")); } @Test public void willCreateAListOfCommaSeparatedString() { final String key = "a csv list property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setKey(key) .setDefaultValue(Collections.emptyList()) .setDynamic(true) .buildList(String.class); JiveGlobals.setProperty(key, "3,2,1"); assertThat(property.getValue(), is(Arrays.asList("3", "2", "1"))); } @Test public void willCreateAListOfCommaWhitespaceSeparatedString() { final String key = "a whitespace csv list property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setKey(key) .setDefaultValue(Collections.emptyList()) .setDynamic(true) .buildList(String.class); JiveGlobals.setProperty(key, " 3, 2, 1 "); assertThat(property.getValue(), is(Arrays.asList("3", "2", "1"))); } @Test public void willCreateAListOfCommaWhitespaceSeparatedString2() { final String key = "another whitespace csv list property"; final SystemProperty> property = SystemProperty.Builder.ofType(List.class) .setKey(key) .setDefaultValue(Collections.emptyList()) .setDynamic(true) .buildList(String.class); JiveGlobals.setProperty(key, "1 , 2 , 3"); assertThat(property.getValue(), is(Arrays.asList("1", "2", "3"))); } @Test public void willCreateASetOfStringProperties() { final String key = "a set property"; final SystemProperty> property = SystemProperty.Builder.ofType(Set.class) .setKey(key) .setSorted(true) .setDefaultValue(Collections.emptySet()) .setDynamic(true) .buildSet(String.class); assertThat(property.getValue(), is(Collections.emptySet())); property.setValue(new HashSet<>(Arrays.asList("3", "2", "1", "2"))); assertThat(property.getValue(), is(new LinkedHashSet<>(Arrays.asList("1", "2", "3")))); assertThat(property.getDisplayValue(), is("1,2,3")); assertThat(JiveGlobals.getProperty(key), is("1,2,3")); JiveGlobals.setProperty(key, "3,2,1,3"); assertThat(property.getValue(), is(new LinkedHashSet<>(Arrays.asList("1", "2", "3")))); assertThat(property.getDisplayValue(), is("1,2,3")); } @Test public void willBuildAJIDProperty() { final String jidString = "test-user@test-domain/test-resource"; final JID jid = new JID(jidString); final String key = "a jid property"; final SystemProperty property = SystemProperty.Builder.ofType(JID.class) .setKey(key) .setDynamic(true) .build(); assertThat(property.getValue(),is(nullValue())); property.setValue(jid); assertThat(property.getValue(),is(jid)); assertThat(property.getDisplayValue(), is(jidString)); assertThat(JiveGlobals.getProperty(key), is(jidString)); } @Test public void willReturnNullForInvalidJID() { final String jidString = "@test-domain"; final String key = "an invalid jid property"; final SystemProperty property = SystemProperty.Builder.ofType(JID.class) .setKey(key) .setDynamic(true) .build(); assertThat(property.getValue(),is(nullValue())); JiveGlobals.setProperty(key, jidString); assertThat(property.getValue(),is(nullValue())); } private enum TestEnum { TEST_1, TEST_2 } @Test public void willBuildAnEnumProperty() { final String key = "an enum property"; final SystemProperty property = SystemProperty.Builder.ofType(TestEnum.class) .setKey(key) .setDefaultValue(TestEnum.TEST_1) .setDynamic(true) .build(); assertThat(property.getValue(),is(TestEnum.TEST_1)); property.setValue(TestEnum.TEST_2); assertThat(property.getValue(),is(TestEnum.TEST_2)); assertThat(property.getDisplayValue(), is("TEST_2")); assertThat(JiveGlobals.getProperty(key), is("TEST_2")); } }