/*
* Copyright Terracotta, Inc.
*
* 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.ehcache.clustered.client.internal.config.xml;
import org.ehcache.clustered.client.config.ClusteringServiceConfiguration;
import org.ehcache.clustered.client.config.TimeoutDuration;
import org.ehcache.config.Configuration;
import org.ehcache.core.internal.util.ClassLoading;
import org.ehcache.core.spi.service.ServiceUtils;
import org.ehcache.spi.service.ServiceCreationConfiguration;
import org.ehcache.xml.CacheManagerServiceConfigurationParser;
import org.ehcache.xml.XmlConfiguration;
import org.ehcache.xml.exceptions.XmlConfigurationException;
import org.ehcache.xml.model.TimeType;
import org.hamcrest.Matchers;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.net.URL;
import java.util.Collection;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import static org.ehcache.xml.XmlModel.convertToJavaTimeUnit;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.*;
/**
* Basic tests for {@link ClusteringServiceConfigurationParser}.
*/
public class ClusteringServiceConfigurationParserTest {
@ClassRule
public static final TemporaryFolder folder = new TemporaryFolder();
@Rule
public final TestName testName = new TestName();
/**
* Ensures the {@link ClusteringServiceConfigurationParser} is locatable as a
* {@link CacheManagerServiceConfigurationParser} instance.
*/
@Test
public void testServiceLocator() throws Exception {
final String expectedParser = ClusteringServiceConfigurationParser.class.getName();
final ServiceLoader<CacheManagerServiceConfigurationParser> parsers =
ClassLoading.libraryServiceLoaderFor(CacheManagerServiceConfigurationParser.class);
foundParser: {
for (final CacheManagerServiceConfigurationParser parser : parsers) {
if (parser.getClass().getName().equals(expectedParser)) {
break foundParser;
}
}
fail("Expected parser not found");
}
}
/**
* Ensures the namespace declared by {@link ClusteringServiceConfigurationParser} and its
* schema are the same.
*/
@Test
public void testSchema() throws Exception {
final ClusteringServiceConfigurationParser parser = new ClusteringServiceConfigurationParser();
final StreamSource schemaSource = (StreamSource) parser.getXmlSchema();
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(true);
final DocumentBuilder domBuilder = factory.newDocumentBuilder();
final Element schema = domBuilder.parse(schemaSource.getInputStream()).getDocumentElement();
final Attr targetNamespaceAttr = schema.getAttributeNode("targetNamespace");
assertThat(targetNamespaceAttr, is(not(nullValue())));
assertThat(targetNamespaceAttr.getValue(), is(parser.getNamespace().toString()));
}
@Test
public void testGetTimeout() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" <tc:read-timeout unit=\"minutes\">5</tc:read-timeout>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
final Configuration configuration = new XmlConfiguration(makeConfig(config));
Collection<ServiceCreationConfiguration<?>> serviceCreationConfigurations =
configuration.getServiceCreationConfigurations();
assertThat(serviceCreationConfigurations, is(not(Matchers.empty())));
ClusteringServiceConfiguration clusteringServiceConfiguration =
ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations);
assertThat(clusteringServiceConfiguration, is(notNullValue()));
assertThat(clusteringServiceConfiguration.getReadOperationTimeout(), is(equalTo(TimeoutDuration.of(5, TimeUnit.MINUTES))));
}
@Test
public void testGetTimeoutNone() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
final Configuration configuration = new XmlConfiguration(makeConfig(config));
Collection<ServiceCreationConfiguration<?>> serviceCreationConfigurations =
configuration.getServiceCreationConfigurations();
assertThat(serviceCreationConfigurations, is(not(Matchers.empty())));
ClusteringServiceConfiguration clusteringServiceConfiguration =
ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations);
assertThat(clusteringServiceConfiguration, is(notNullValue()));
assertThat(clusteringServiceConfiguration.getReadOperationTimeout(), is(TimeoutDuration.of(20, TimeUnit.SECONDS)));
}
@Test
public void testGetTimeoutUnitDefault() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" <tc:read-timeout>5</tc:read-timeout>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
final Configuration configuration = new XmlConfiguration(makeConfig(config));
Collection<ServiceCreationConfiguration<?>> serviceCreationConfigurations =
configuration.getServiceCreationConfigurations();
assertThat(serviceCreationConfigurations, is(not(Matchers.empty())));
ClusteringServiceConfiguration clusteringServiceConfiguration =
ServiceUtils.findSingletonAmongst(ClusteringServiceConfiguration.class, serviceCreationConfigurations);
assertThat(clusteringServiceConfiguration, is(notNullValue()));
TimeUnit defaultUnit = convertToJavaTimeUnit(new TimeType().getUnit());
assertThat(clusteringServiceConfiguration.getReadOperationTimeout(), is(equalTo(TimeoutDuration.of(5, defaultUnit))));
}
@Test
public void testGetTimeoutUnitBad() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" <tc:read-timeout unit=\"femtos\">5</tc:read-timeout>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
try {
new XmlConfiguration(makeConfig(config));
fail("Expecting XmlConfigurationException");
} catch (XmlConfigurationException e) {
assertThat(e.getMessage(), containsString("Error parsing XML configuration "));
assertThat(e.getCause().getMessage(), containsString("Value 'femtos' is not facet-valid with respect to enumeration "));
}
}
@Test
public void testGetTimeoutValueTooBig() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" <tc:read-timeout unit=\"seconds\">"
+ BigInteger.ONE.add(BigInteger.valueOf(Long.MAX_VALUE))
+ "</tc:read-timeout>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
try {
new XmlConfiguration(makeConfig(config));
fail("Expecting XmlConfigurationException");
} catch (XmlConfigurationException e) {
assertThat(e.getMessage(), containsString(" exceeds allowed value "));
}
}
@Test
public void testGetTimeoutValueOmitted() throws Exception {
final String[] config = new String[]
{
"<ehcache:config",
" xmlns:ehcache=\"http://www.ehcache.org/v3\"",
" xmlns:tc=\"http://www.ehcache.org/v3/clustered\">",
"",
" <ehcache:service>",
" <tc:cluster>",
" <tc:connection url=\"terracotta://example.com:9540/cachemanager\"/>",
" <tc:read-timeout unit=\"seconds\"></tc:read-timeout>",
" </tc:cluster>",
" </ehcache:service>",
"",
"</ehcache:config>"
};
try {
new XmlConfiguration(makeConfig(config));
fail("Expecting XmlConfigurationException");
} catch (XmlConfigurationException e) {
assertThat(e.getMessage(), containsString("Error parsing XML configuration "));
assertThat(e.getCause().getMessage(), containsString("'' is not a valid value for 'integer'"));
}
}
/**
* Constructs a temporary XML configuration file.
*
* @param lines the lines to include in the XML configuration file
*
* @return a {@code URL} pointing to the XML configuration file
*
* @throws IOException if an error is raised while creating or writing the XML configuration file
*/
@SuppressWarnings("ThrowFromFinallyBlock")
private URL makeConfig(final String[] lines) throws IOException {
final File configFile = folder.newFile(testName.getMethodName() + "_config.xml");
OutputStreamWriter out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(configFile), "UTF-8");
for (final String line : lines) {
out.write(line);
}
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
return configFile.toURI().toURL();
}
}