/*
* Copyright (c) 2008-2017, Hazelcast, Inc. 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 com.hazelcast.config;
import com.hazelcast.test.HazelcastSerialClassRunner;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
import static com.hazelcast.config.XMLConfigBuilderTest.HAZELCAST_END_TAG;
import static com.hazelcast.config.XMLConfigBuilderTest.HAZELCAST_START_TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(HazelcastSerialClassRunner.class)
@Category(QuickTest.class)
public class XmlConfigImportVariableReplacementTest {
@Rule
public ExpectedException rule = ExpectedException.none();
@Test
public void testImportElementOnlyAppearsInTopLevel() {
String xml = HAZELCAST_START_TAG
+ " <network>"
+ " <import resource=\"\"/>\n"
+ " </network>"
+ HAZELCAST_END_TAG;
expectInvalid();
buildConfig(xml, null);
}
@Test
public void testHazelcastElementOnlyAppearsOnce() {
String xml = HAZELCAST_START_TAG
+ " <hazelcast>"
+ " </hazelcast>"
+ HAZELCAST_END_TAG;
expectInvalid();
buildConfig(xml, null);
}
@Test
public void readVariables() {
String xml = HAZELCAST_START_TAG
+ " <semaphore name=\"${name}\">\n"
+ " <initial-permits>${initial.permits}</initial-permits>\n"
+ " <backup-count>${backupcount.part1}${backupcount.part2}</backup-count>\n"
+ " </semaphore>"
+ HAZELCAST_END_TAG;
Properties properties = new Properties();
properties.setProperty("name", "s");
properties.setProperty("initial.permits", "25");
properties.setProperty("backupcount.part1", "0");
properties.setProperty("backupcount.part2", "6");
Config config = buildConfig(xml, properties);
SemaphoreConfig semaphoreConfig = config.getSemaphoreConfig("s");
assertEquals(25, semaphoreConfig.getInitialPermits());
assertEquals(6, semaphoreConfig.getBackupCount());
assertEquals(0, semaphoreConfig.getAsyncBackupCount());
}
@Test
public void testImportConfigFromResourceVariables() throws Exception {
File file = createConfigFile("foo", "bar");
FileOutputStream os = new FileOutputStream(file);
String networkConfig = HAZELCAST_START_TAG
+ " <network>\n"
+ " <join>\n"
+ " <multicast enabled=\"false\"/>\n"
+ " <tcp-ip enabled=\"true\"/>\n"
+ " </join>\n"
+ " </network>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os, networkConfig);
String xml = HAZELCAST_START_TAG
+ " <import resource=\"${config.location}\"/>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, "config.location", file.getAbsolutePath());
JoinConfig join = config.getNetworkConfig().getJoin();
assertFalse(join.getMulticastConfig().isEnabled());
assertTrue(join.getTcpIpConfig().isEnabled());
}
@Test
public void testImportedConfigVariableReplacement() throws Exception {
File file = createConfigFile("foo", "bar");
FileOutputStream os = new FileOutputStream(file);
String networkConfig = HAZELCAST_START_TAG
+ " <network>\n"
+ " <join>\n"
+ " <multicast enabled=\"false\"/>\n"
+ " <tcp-ip enabled=\"${tcp.ip.enabled}\"/>\n"
+ " </join>\n"
+ " </network>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os, networkConfig);
String xml = HAZELCAST_START_TAG
+ " <import resource=\"${config.location}\"/>\n"
+ HAZELCAST_END_TAG;
Properties properties = new Properties();
properties.setProperty("config.location", file.getAbsolutePath());
properties.setProperty("tcp.ip.enabled", "true");
Config config = buildConfig(xml, properties);
JoinConfig join = config.getNetworkConfig().getJoin();
assertFalse(join.getMulticastConfig().isEnabled());
assertTrue(join.getTcpIpConfig().isEnabled());
}
@Test
public void testTwoResourceCyclicImportThrowsException() throws Exception {
File config1 = createConfigFile("hz1", "xml");
File config2 = createConfigFile("hz2", "xml");
FileOutputStream os1 = new FileOutputStream(config1);
FileOutputStream os2 = new FileOutputStream(config2);
String config1Xml = HAZELCAST_START_TAG
+ " <import resource=\"file:///" + config2.getAbsolutePath() + "\"/>\n"
+ HAZELCAST_END_TAG;
String config2Xml = HAZELCAST_START_TAG
+ " <import resource=\"file:///" + config1.getAbsolutePath() + "\"/>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os1, config1Xml);
writeStringToStreamAndClose(os2, config2Xml);
expectInvalid();
buildConfig(config1Xml, null);
}
@Test
public void testThreeResourceCyclicImportThrowsException() throws Exception {
String template = HAZELCAST_START_TAG
+ " <import resource=\"file:///%s\"/>\n"
+ HAZELCAST_END_TAG;
File config1 = createConfigFile("hz1", "xml");
File config2 = createConfigFile("hz2", "xml");
File config3 = createConfigFile("hz3", "xml");
String config1Xml = String.format(template, config2.getAbsolutePath());
String config2Xml = String.format(template, config3.getAbsolutePath());
String config3Xml = String.format(template, config1.getAbsolutePath());
writeStringToStreamAndClose(new FileOutputStream(config1), config1Xml);
writeStringToStreamAndClose(new FileOutputStream(config2), config2Xml);
writeStringToStreamAndClose(new FileOutputStream(config3), config3Xml);
expectInvalid();
buildConfig(config1Xml, null);
}
@Test
public void testImportEmptyResourceContent() throws Exception {
File config1 = createConfigFile("hz1", "xml");
FileOutputStream os1 = new FileOutputStream(config1);
String config1Xml = HAZELCAST_START_TAG
+ " <import resource='file:///" + config1.getAbsolutePath() + "'/>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os1, "");
expectInvalid();
buildConfig(config1Xml, null);
}
@Test
public void testImportEmptyResourceThrowsException() {
String xml = HAZELCAST_START_TAG
+ " <import resource=\"\"/>\n"
+ HAZELCAST_END_TAG;
expectInvalid();
buildConfig(xml, null);
}
@Test
public void testImportNotExistingResourceThrowsException() {
expectInvalid();
String xml = HAZELCAST_START_TAG
+ " <import resource=\"notexisting.xml\"/>\n"
+ HAZELCAST_END_TAG;
buildConfig(xml, null);
}
@Test
public void testImportNetworkConfigFromFile() throws Exception {
File file = createConfigFile("foo", "bar");
FileOutputStream os = new FileOutputStream(file);
String networkConfig = HAZELCAST_START_TAG
+ " <network>\n"
+ " <join>\n"
+ " <multicast enabled=\"false\"/>\n"
+ " <tcp-ip enabled=\"true\"/>\n"
+ " </join>\n"
+ " </network>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os, networkConfig);
String xml = HAZELCAST_START_TAG
+ " <import resource=\"file:///" + file.getAbsolutePath() + "\"/>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, null);
JoinConfig join = config.getNetworkConfig().getJoin();
assertFalse(join.getMulticastConfig().isEnabled());
assertTrue(join.getTcpIpConfig().isEnabled());
}
@Test
public void testImportMapConfigFromFile() throws Exception {
File file = createConfigFile("mymap", "config");
FileOutputStream os = new FileOutputStream(file);
String mapConfig = HAZELCAST_START_TAG
+ " <map name=\"mymap\">\n"
+ " <backup-count>6</backup-count>"
+ " <time-to-live-seconds>10</time-to-live-seconds>"
+ " <map-store enabled=\"true\" initial-mode=\"LAZY\">\n"
+ " <class-name>com.hazelcast.examples.MyMapStore</class-name>\n"
+ " <write-delay-seconds>10</write-delay-seconds>\n"
+ " <write-batch-size>100</write-batch-size>\n"
+ " </map-store>"
+ "</map>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os, mapConfig);
String xml = HAZELCAST_START_TAG
+ " <import resource=\"file:///" + file.getAbsolutePath() + "\"/>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, null);
MapConfig myMapConfig = config.getMapConfig("mymap");
assertEquals("mymap", myMapConfig.getName());
assertEquals(6, myMapConfig.getBackupCount());
assertEquals(10, myMapConfig.getTimeToLiveSeconds());
MapStoreConfig myMapStoreConfig = myMapConfig.getMapStoreConfig();
assertEquals(10, myMapStoreConfig.getWriteDelaySeconds());
assertEquals(100, myMapStoreConfig.getWriteBatchSize());
assertEquals("com.hazelcast.examples.MyMapStore", myMapStoreConfig.getClassName());
}
@Test
public void testImportGroupConfigFromClassPath() {
String xml = HAZELCAST_START_TAG
+ " <import resource=\"classpath:test-hazelcast.xml\"/>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, null);
GroupConfig groupConfig = config.getGroupConfig();
assertEquals("foobar", groupConfig.getName());
assertEquals("dev-pass", groupConfig.getPassword());
}
@Test
public void testXmlDeniesDuplicateNetworkConfig() {
expectDuplicateElementError("network");
String networkConfig = " <network>\n"
+ " <join>\n"
+ " <multicast enabled=\"false\"/>\n"
+ " <tcp-ip enabled=\"true\"/>\n"
+ " </join>\n"
+ " </network>\n";
buildConfig(HAZELCAST_START_TAG + networkConfig + networkConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateGroupConfig() {
expectDuplicateElementError("group");
String groupConfig = " <group>\n"
+ " <name>foobar</name>\n"
+ " <password>dev-pass</password>\n"
+ " </group>\n";
buildConfig(HAZELCAST_START_TAG + groupConfig + groupConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateLicenseKeyConfig() {
expectDuplicateElementError("license-key");
String licenseConfig = " <license-key>foo</license-key>";
buildConfig(HAZELCAST_START_TAG + licenseConfig + licenseConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicatePropertiesConfig() {
expectDuplicateElementError("properties");
String propertiesConfig = " <properties>\n"
+ " <property name='foo'>fooval</property>\n"
+ " </properties>\n";
buildConfig(HAZELCAST_START_TAG + propertiesConfig + propertiesConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicatePartitionGroupConfig() {
expectDuplicateElementError("partition-group");
String partitionConfig = " <partition-group>\n"
+ " <member-group>\n"
+ " <interface>foo</interface>\n"
+ " </member-group>\n"
+ " </partition-group>\n";
buildConfig(HAZELCAST_START_TAG + partitionConfig + partitionConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateListenersConfig() {
expectDuplicateElementError("listeners");
String listenersConfig = " <listeners>"
+ " <listener>foo</listener>\n\n"
+ " </listeners>\n";
buildConfig(HAZELCAST_START_TAG + listenersConfig + listenersConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateSerializationConfig() {
expectDuplicateElementError("serialization");
String serializationConfig = " <serialization>\n"
+ " <portable-version>0</portable-version>\n"
+ " <data-serializable-factories>\n"
+ " <data-serializable-factory factory-id=\"1\">com.hazelcast.examples.DataSerializableFactory\n"
+ " </data-serializable-factory>\n"
+ " </data-serializable-factories>\n"
+ " <portable-factories>\n"
+ " <portable-factory factory-id=\"1\">com.hazelcast.examples.PortableFactory</portable-factory>\n"
+ " </portable-factories>\n"
+ " <serializers>\n"
+ " <global-serializer>com.hazelcast.examples.GlobalSerializerFactory</global-serializer>\n"
+ " <serializer type-class=\"com.hazelcast.examples.DummyType\"\n"
+ " class-name=\"com.hazelcast.examples.SerializerFactory\"/>\n"
+ " </serializers>\n"
+ " <check-class-def-errors>true</check-class-def-errors>\n"
+ " </serialization>\n";
buildConfig(HAZELCAST_START_TAG + serializationConfig + serializationConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateServicesConfig() {
expectDuplicateElementError("services");
String servicesConfig = " <services> "
+ " <service enabled=\"true\">\n"
+ " <name>custom-service</name>\n"
+ " <class-name>com.hazelcast.examples.MyService</class-name>\n"
+ " </service>\n"
+ " </services>";
buildConfig(HAZELCAST_START_TAG + servicesConfig + servicesConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateSecurityConfig() {
expectDuplicateElementError("security");
String securityConfig = " <security/>\n";
buildConfig(HAZELCAST_START_TAG + securityConfig + securityConfig + HAZELCAST_END_TAG, null);
}
@Test
public void testXmlDeniesDuplicateMemberAttributesConfig() {
expectDuplicateElementError("member-attributes");
String memberAttConfig = " <member-attributes>\n"
+ " <attribute name=\"attribute.float\" type=\"float\">1234.5678</attribute>\n"
+ " </member-attributes>\n";
buildConfig(HAZELCAST_START_TAG + memberAttConfig + memberAttConfig + HAZELCAST_END_TAG, null);
}
private void expectDuplicateElementError(String elName) {
expectInvalid();
}
@Test
public void testXmlVariableReplacementAsSubstring() {
String xml = HAZELCAST_START_TAG
+ " <properties>\n"
+ " <property name=\"${env}-with-suffix\">local-with-suffix</property>\n"
+ " <property name=\"with-prefix-${env}\">with-prefix-local</property>\n"
+ " </properties>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, "env", "local");
assertEquals(config.getProperty("local-with-suffix"), "local-with-suffix");
assertEquals(config.getProperty("with-prefix-local"), "with-prefix-local");
}
@Test
public void testXmlImportWithVariableReplacementAsSubstring() throws Exception {
File file = createConfigFile("foo", "bar");
FileOutputStream os = new FileOutputStream(file);
String networkConfig = HAZELCAST_START_TAG
+ " <properties>\n"
+ " <property name=\"prop1\">value1</property>\n"
+ " <property name=\"prop2\">value2</property>\n"
+ " </properties>\n"
+ HAZELCAST_END_TAG;
writeStringToStreamAndClose(os, networkConfig);
String xml = HAZELCAST_START_TAG
+ " <import resource=\"file:///" + "${file}" + "\"/>\n"
+ HAZELCAST_END_TAG;
Config config = buildConfig(xml, "file", file.getAbsolutePath());
assertEquals(config.getProperty("prop1"), "value1");
assertEquals(config.getProperty("prop2"), "value2");
}
private void expectInvalid() {
InvalidConfigurationTest.expectInvalid(rule);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static File createConfigFile(String filename, String suffix) throws Exception {
File file = File.createTempFile(filename, suffix);
file.setWritable(true);
file.deleteOnExit();
return file;
}
private static void writeStringToStreamAndClose(FileOutputStream os, String string) throws Exception {
os.write(string.getBytes());
os.flush();
os.close();
}
private static Config buildConfig(String xml, Properties properties) {
ByteArrayInputStream bis = new ByteArrayInputStream(xml.getBytes());
XmlConfigBuilder configBuilder = new XmlConfigBuilder(bis);
configBuilder.setProperties(properties);
return configBuilder.build();
}
private static Config buildConfig(String xml, String key, String value) {
Properties properties = new Properties();
properties.setProperty(key, value);
return buildConfig(xml, properties);
}
}