You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by du...@apache.org on 2022/06/20 14:11:39 UTC
[shardingsphere] branch master updated: Refactor CreateTrafficRuleHandler and AlterTrafficRuleHandler (#18464)
This is an automated email from the ASF dual-hosted git repository.
duanzhengqiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new d7636d89d3d Refactor CreateTrafficRuleHandler and AlterTrafficRuleHandler (#18464)
d7636d89d3d is described below
commit d7636d89d3dde7768bd71ac0c2b1104563a4c877
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Mon Jun 20 22:11:29 2022 +0800
Refactor CreateTrafficRuleHandler and AlterTrafficRuleHandler (#18464)
---
.../common/updatable/AlterTrafficRuleHandler.java | 13 +++--
.../common/updatable/CreateTrafficRuleHandler.java | 39 +++++++++-----
.../updatable/AlterTrafficRuleHandlerTest.java | 35 +++++++-----
.../updatable/CreateTrafficRuleHandlerTest.java | 62 ++++++++++++----------
4 files changed, 90 insertions(+), 59 deletions(-)
diff --git a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandler.java b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandler.java
index eec18bb7a4c..6adbb794efb 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandler.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandler.java
@@ -34,12 +34,12 @@ import org.apache.shardingsphere.traffic.api.config.TrafficStrategyConfiguration
import org.apache.shardingsphere.traffic.factory.TrafficAlgorithmFactory;
import org.apache.shardingsphere.traffic.factory.TrafficLoadBalanceAlgorithmFactory;
import org.apache.shardingsphere.traffic.rule.TrafficRule;
+import org.apache.shardingsphere.traffic.rule.TrafficStrategyRule;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
-import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -60,13 +60,16 @@ public final class AlterTrafficRuleHandler extends UpdatableRALBackendHandler<Al
}
private void checkRuleNames() throws DistSQLException {
- TrafficRuleConfiguration currentConfig = ProxyContext
- .getInstance().getContextManager().getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TrafficRule.class).getConfiguration();
- Collection<String> currentRuleNames = currentConfig.getTrafficStrategies().stream().map(TrafficStrategyConfiguration::getName).collect(Collectors.toSet());
- Set<String> notExistRuleNames = getSqlStatement().getSegments().stream().map(TrafficRuleSegment::getName).filter(each -> !currentRuleNames.contains(each)).collect(Collectors.toSet());
+ Collection<String> notExistRuleNames = getNotExistRuleNames();
DistSQLException.predictionThrow(notExistRuleNames.isEmpty(), () -> new RequiredRuleMissedException("Traffic", notExistRuleNames));
}
+ private Collection<String> getNotExistRuleNames() {
+ TrafficRule currentRule = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TrafficRule.class);
+ Collection<String> currentRuleNames = currentRule.getStrategyRules().stream().map(TrafficStrategyRule::getName).collect(Collectors.toSet());
+ return getSqlStatement().getSegments().stream().map(TrafficRuleSegment::getName).filter(each -> !currentRuleNames.contains(each)).collect(Collectors.toSet());
+ }
+
private void checkAlgorithmNames() throws DistSQLException {
Collection<String> invalidAlgorithmNames = getInvalidAlgorithmNames();
DistSQLException.predictionThrow(invalidAlgorithmNames.isEmpty(), () -> new InvalidAlgorithmConfigurationException("Traffic", invalidAlgorithmNames));
diff --git a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandler.java b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandler.java
index 38c3765458b..fafbb1fb547 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandler.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandler.java
@@ -30,15 +30,14 @@ import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
import org.apache.shardingsphere.proxy.backend.text.distsql.ral.UpdatableRALBackendHandler;
import org.apache.shardingsphere.proxy.backend.text.distsql.ral.common.convert.TrafficRuleConverter;
import org.apache.shardingsphere.traffic.api.config.TrafficRuleConfiguration;
-import org.apache.shardingsphere.traffic.api.config.TrafficStrategyConfiguration;
import org.apache.shardingsphere.traffic.factory.TrafficAlgorithmFactory;
import org.apache.shardingsphere.traffic.factory.TrafficLoadBalanceAlgorithmFactory;
import org.apache.shardingsphere.traffic.rule.TrafficRule;
+import org.apache.shardingsphere.traffic.rule.TrafficStrategyRule;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Optional;
-import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -49,28 +48,42 @@ public final class CreateTrafficRuleHandler extends UpdatableRALBackendHandler<C
@Override
protected void update(final ContextManager contextManager) throws DistSQLException {
TrafficRule trafficRule = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TrafficRule.class);
- checkTrafficRuleConfiguration(trafficRule.getConfiguration());
- checkInvalidAlgorithmNames();
+ check();
updateToRepository(TrafficRuleConverter.convert(getSqlStatement().getSegments()), trafficRule.getConfiguration());
}
- private void checkTrafficRuleConfiguration(final TrafficRuleConfiguration trafficRuleConfig) throws DistSQLException {
- Collection<String> currentRuleNames = trafficRuleConfig.getTrafficStrategies().stream().map(TrafficStrategyConfiguration::getName).collect(Collectors.toSet());
- Set<String> duplicatedRuleNames = getSqlStatement().getSegments().stream().map(TrafficRuleSegment::getName).filter(currentRuleNames::contains).collect(Collectors.toSet());
- DistSQLException.predictionThrow(duplicatedRuleNames.isEmpty(), () -> new DuplicateRuleException("traffic", duplicatedRuleNames));
+ private void check() throws DistSQLException {
+ checkRuleNames();
+ checkAlgorithmNames();
}
- private void checkInvalidAlgorithmNames() throws DistSQLException {
- Collection<String> invalidAlgorithmNames = new LinkedList<>();
+ private void checkRuleNames() throws DistSQLException {
+ Collection<String> inUsedRuleNames = getInUsedRuleNames();
+ DistSQLException.predictionThrow(inUsedRuleNames.isEmpty(), () -> new DuplicateRuleException("Traffic", inUsedRuleNames));
+ }
+
+ private Collection<String> getInUsedRuleNames() {
+ TrafficRule currentRule = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TrafficRule.class);
+ Collection<String> currentRuleNames = currentRule.getStrategyRules().stream().map(TrafficStrategyRule::getName).collect(Collectors.toSet());
+ return getSqlStatement().getSegments().stream().map(TrafficRuleSegment::getName).filter(currentRuleNames::contains).collect(Collectors.toSet());
+ }
+
+ private void checkAlgorithmNames() throws DistSQLException {
+ Collection<String> invalidAlgorithmNames = getInvalidAlgorithmNames();
+ DistSQLException.predictionThrow(invalidAlgorithmNames.isEmpty(), () -> new InvalidAlgorithmConfigurationException("Traffic", invalidAlgorithmNames));
+ }
+
+ private Collection<String> getInvalidAlgorithmNames() {
+ Collection<String> result = new LinkedList<>();
for (TrafficRuleSegment each : getSqlStatement().getSegments()) {
if (!TrafficAlgorithmFactory.contains(each.getAlgorithm().getName())) {
- invalidAlgorithmNames.add(each.getAlgorithm().getName());
+ result.add(each.getAlgorithm().getName());
}
if (null != each.getLoadBalancer() && !TrafficLoadBalanceAlgorithmFactory.contains(each.getLoadBalancer().getName())) {
- invalidAlgorithmNames.add(each.getLoadBalancer().getName());
+ result.add(each.getLoadBalancer().getName());
}
}
- DistSQLException.predictionThrow(invalidAlgorithmNames.isEmpty(), () -> new InvalidAlgorithmConfigurationException("traffic", invalidAlgorithmNames));
+ return result;
}
private void updateToRepository(final TrafficRuleConfiguration toBeCreatedRuleConfig, final TrafficRuleConfiguration currentRuleConfig) {
diff --git a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandlerTest.java b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandlerTest.java
index e36d2cc6c94..b0f33f7bd98 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandlerTest.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/AlterTrafficRuleHandlerTest.java
@@ -30,6 +30,7 @@ import org.apache.shardingsphere.proxy.backend.util.ProxyContextRestorer;
import org.apache.shardingsphere.traffic.api.config.TrafficRuleConfiguration;
import org.apache.shardingsphere.traffic.api.config.TrafficStrategyConfiguration;
import org.apache.shardingsphere.traffic.rule.TrafficRule;
+import org.apache.shardingsphere.traffic.rule.TrafficStrategyRule;
import org.junit.Test;
import java.sql.SQLException;
@@ -43,23 +44,23 @@ import static org.mockito.Mockito.when;
public final class AlterTrafficRuleHandlerTest extends ProxyContextRestorer {
- @Test(expected = InvalidAlgorithmConfigurationException.class)
- public void assertExecuteWithInvalidAlgorithmType() throws SQLException {
+ @Test(expected = RequiredRuleMissedException.class)
+ public void assertExecuteWithNotExistRuleName() throws SQLException {
mockContextManager();
TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment(
- "rule_name_1", Arrays.asList("olap", "order_by"), new AlgorithmSegment("invalid", new Properties()), new AlgorithmSegment("invalid", new Properties()));
+ "rule_name_3", Arrays.asList("olap", "order_by"), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
AlterTrafficRuleHandler handler = new AlterTrafficRuleHandler();
- handler.init(getSQLStatement(trafficRuleSegment), null);
+ handler.init(new AlterTrafficRuleStatement(Collections.singleton(trafficRuleSegment)), null);
handler.execute();
}
- @Test(expected = RequiredRuleMissedException.class)
- public void assertExecuteWithNotExistRuleName() throws SQLException {
+ @Test(expected = InvalidAlgorithmConfigurationException.class)
+ public void assertExecuteWithInvalidAlgorithmType() throws SQLException {
mockContextManager();
TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment(
- "rule_name_3", Arrays.asList("olap", "order_by"), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
+ "rule_name_1", Arrays.asList("olap", "order_by"), new AlgorithmSegment("invalid", new Properties()), new AlgorithmSegment("invalid", new Properties()));
AlterTrafficRuleHandler handler = new AlterTrafficRuleHandler();
- handler.init(getSQLStatement(trafficRuleSegment), null);
+ handler.init(new AlterTrafficRuleStatement(Collections.singleton(trafficRuleSegment)), null);
handler.execute();
}
@@ -71,20 +72,30 @@ public final class AlterTrafficRuleHandlerTest extends ProxyContextRestorer {
TrafficRuleSegment trafficRuleSegment2 = new TrafficRuleSegment(
"rule_name_2", Collections.emptyList(), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
AlterTrafficRuleHandler handler = new AlterTrafficRuleHandler();
- handler.init(getSQLStatement(trafficRuleSegment1, trafficRuleSegment2), null);
+ handler.init(new AlterTrafficRuleStatement(Arrays.asList(trafficRuleSegment1, trafficRuleSegment2)), null);
handler.execute();
}
private void mockContextManager() {
ContextManager contextManager = mock(ContextManager.class, RETURNS_DEEP_STUBS);
ShardingSphereRuleMetaData globalRuleMetaData = mock(ShardingSphereRuleMetaData.class);
- TrafficRule rule = mock(TrafficRule.class);
+ TrafficRule rule = mockTrafficRule();
when(rule.getConfiguration()).thenReturn(createTrafficRuleConfiguration());
when(globalRuleMetaData.getSingleRule(TrafficRule.class)).thenReturn(rule);
when(contextManager.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(globalRuleMetaData);
ProxyContext.init(contextManager);
}
+ private TrafficRule mockTrafficRule() {
+ TrafficRule result = mock(TrafficRule.class);
+ TrafficStrategyRule strategyRule1 = mock(TrafficStrategyRule.class);
+ when(strategyRule1.getName()).thenReturn("rule_name_1");
+ TrafficStrategyRule strategyRule2 = mock(TrafficStrategyRule.class);
+ when(strategyRule2.getName()).thenReturn("rule_name_2");
+ when(result.getStrategyRules()).thenReturn(Arrays.asList(strategyRule1, strategyRule2));
+ return result;
+ }
+
private TrafficRuleConfiguration createTrafficRuleConfiguration() {
TrafficRuleConfiguration result = new TrafficRuleConfiguration();
result.getTrafficStrategies().add(new TrafficStrategyConfiguration("rule_name_1", Arrays.asList("olap", "order_by"), "algorithm_1", "load_balancer_1"));
@@ -101,8 +112,4 @@ public final class AlterTrafficRuleHandlerTest extends ProxyContextRestorer {
result.put("sql", "select * from t_order");
return result;
}
-
- private AlterTrafficRuleStatement getSQLStatement(final TrafficRuleSegment... segments) {
- return new AlterTrafficRuleStatement(Arrays.asList(segments));
- }
}
diff --git a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandlerTest.java b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandlerTest.java
index 77c20324954..490179a3a69 100644
--- a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandlerTest.java
+++ b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/text/distsql/ral/common/updatable/CreateTrafficRuleHandlerTest.java
@@ -30,6 +30,7 @@ import org.apache.shardingsphere.proxy.backend.util.ProxyContextRestorer;
import org.apache.shardingsphere.traffic.api.config.TrafficRuleConfiguration;
import org.apache.shardingsphere.traffic.api.config.TrafficStrategyConfiguration;
import org.apache.shardingsphere.traffic.rule.TrafficRule;
+import org.apache.shardingsphere.traffic.rule.TrafficStrategyRule;
import org.junit.Test;
import java.sql.SQLException;
@@ -43,55 +44,62 @@ import static org.mockito.Mockito.when;
public final class CreateTrafficRuleHandlerTest extends ProxyContextRestorer {
- @Test(expected = InvalidAlgorithmConfigurationException.class)
- public void assertCheckWithInvalidAlgorithmType() throws SQLException {
- ContextManager contextManager = mockContextManager();
- ProxyContext.init(contextManager);
- TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment("input_rule_name", Arrays.asList("olap", "order_by"),
- new AlgorithmSegment("invalid", new Properties()), new AlgorithmSegment("invalid", new Properties()));
+ @Test(expected = DuplicateRuleException.class)
+ public void assertExecuteWithInUsedRuleName() throws SQLException {
+ mockContextManager();
+ TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment(
+ "rule_name_1", Arrays.asList("olap", "order_by"), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
CreateTrafficRuleHandler handler = new CreateTrafficRuleHandler();
- handler.init(new CreateTrafficRuleStatement(Collections.singletonList(trafficRuleSegment)), null);
+ handler.init(new CreateTrafficRuleStatement(Collections.singleton(trafficRuleSegment)), null);
handler.execute();
}
- @Test(expected = DuplicateRuleException.class)
- public void assertCheckWithDuplicatedRuleName() throws SQLException {
- ContextManager contextManager = mockContextManager();
- ProxyContext.init(contextManager);
- TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment("rule_name_1", Arrays.asList("olap", "order_by"),
- new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
+ @Test(expected = InvalidAlgorithmConfigurationException.class)
+ public void assertExecuteWithInvalidAlgorithmType() throws SQLException {
+ mockContextManager();
+ TrafficRuleSegment trafficRuleSegment = new TrafficRuleSegment("input_rule_name", Arrays.asList("olap", "order_by"),
+ new AlgorithmSegment("invalid", new Properties()), new AlgorithmSegment("invalid", new Properties()));
CreateTrafficRuleHandler handler = new CreateTrafficRuleHandler();
- handler.init(new CreateTrafficRuleStatement(Collections.singletonList(trafficRuleSegment)), null);
+ handler.init(new CreateTrafficRuleStatement(Collections.singleton(trafficRuleSegment)), null);
handler.execute();
}
@Test
- public void assertCheckSuccess() throws SQLException {
- ContextManager contextManager = mockContextManager();
- ProxyContext.init(contextManager);
- TrafficRuleSegment trafficRuleSegment1 = new TrafficRuleSegment("rule_name_3", Arrays.asList("olap", "order_by"),
- new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
- TrafficRuleSegment trafficRuleSegment2 = new TrafficRuleSegment("rule_name_4", Collections.emptyList(),
- new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), null);
+ public void assertExecute() throws SQLException {
+ mockContextManager();
+ TrafficRuleSegment trafficRuleSegment1 = new TrafficRuleSegment(
+ "rule_name_3", Arrays.asList("olap", "order_by"), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
+ TrafficRuleSegment trafficRuleSegment2 = new TrafficRuleSegment(
+ "rule_name_4", Collections.emptyList(), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()), new AlgorithmSegment("DISTSQL.FIXTURE", new Properties()));
CreateTrafficRuleHandler handler = new CreateTrafficRuleHandler();
handler.init(new CreateTrafficRuleStatement(Arrays.asList(trafficRuleSegment1, trafficRuleSegment2)), null);
handler.execute();
}
- private ContextManager mockContextManager() {
- ContextManager result = mock(ContextManager.class, RETURNS_DEEP_STUBS);
+ private void mockContextManager() {
+ ContextManager contextManager = mock(ContextManager.class, RETURNS_DEEP_STUBS);
ShardingSphereRuleMetaData globalRuleMetaData = mock(ShardingSphereRuleMetaData.class);
- when(result.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(globalRuleMetaData);
- TrafficRule rule = mock(TrafficRule.class);
+ TrafficRule rule = mockTrafficRule();
when(rule.getConfiguration()).thenReturn(createTrafficRuleConfiguration());
- when(result.getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TrafficRule.class)).thenReturn(rule);
+ when(globalRuleMetaData.getSingleRule(TrafficRule.class)).thenReturn(rule);
+ when(contextManager.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(globalRuleMetaData);
+ ProxyContext.init(contextManager);
+ }
+
+ private TrafficRule mockTrafficRule() {
+ TrafficRule result = mock(TrafficRule.class);
+ TrafficStrategyRule strategyRule1 = mock(TrafficStrategyRule.class);
+ when(strategyRule1.getName()).thenReturn("rule_name_1");
+ TrafficStrategyRule strategyRule2 = mock(TrafficStrategyRule.class);
+ when(strategyRule2.getName()).thenReturn("rule_name_2");
+ when(result.getStrategyRules()).thenReturn(Arrays.asList(strategyRule1, strategyRule2));
return result;
}
private TrafficRuleConfiguration createTrafficRuleConfiguration() {
TrafficRuleConfiguration result = new TrafficRuleConfiguration();
result.getTrafficStrategies().add(new TrafficStrategyConfiguration("rule_name_1", Arrays.asList("olap", "order_by"), "algorithm_1", "load_balancer_1"));
- result.getTrafficStrategies().add(new TrafficStrategyConfiguration("rule_name_2", Collections.singletonList("oltp"), "algorithm_2", "load_balancer_2"));
+ result.getTrafficStrategies().add(new TrafficStrategyConfiguration("rule_name_2", Collections.singleton("oltp"), "algorithm_2", "load_balancer_2"));
result.getTrafficAlgorithms().put("algorithm_1", new ShardingSphereAlgorithmConfiguration("SQL_MATCH", createProperties()));
result.getTrafficAlgorithms().put("algorithm_2", new ShardingSphereAlgorithmConfiguration("SQL_HINT", new Properties()));
result.getLoadBalancers().put("load_balancer_1", new ShardingSphereAlgorithmConfiguration("RANDOM", new Properties()));