Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id AC72E200D6C for ; Wed, 29 Nov 2017 00:58:02 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id AAE25160BE7; Tue, 28 Nov 2017 23:58:02 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 88E81160C16 for ; Wed, 29 Nov 2017 00:58:00 +0100 (CET) Received: (qmail 81892 invoked by uid 500); 28 Nov 2017 23:57:59 -0000 Mailing-List: contact commits-help@geode.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.apache.org Delivered-To: mailing list commits@geode.apache.org Received: (qmail 81787 invoked by uid 99); 28 Nov 2017 23:57:59 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Nov 2017 23:57:59 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id C65AB85622; Tue, 28 Nov 2017 23:57:58 +0000 (UTC) Date: Tue, 28 Nov 2017 23:58:00 +0000 To: "commits@geode.apache.org" Subject: [geode] 02/06: GEODE-3824: add create jdbc-connection command MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit From: klund@apache.org In-Reply-To: <151191347832.3364.17001749714945600343@gitbox.apache.org> References: <151191347832.3364.17001749714945600343@gitbox.apache.org> X-Git-Host: gitbox.apache.org X-Git-Repo: geode X-Git-Refname: refs/heads/feature/GEODE-3781 X-Git-Reftype: branch X-Git-Rev: 148399fb8de0f77293d7fa7a025b2d405c80f832 X-Git-NotificationType: diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated Message-Id: <20171128235758.C65AB85622@gitbox.apache.org> archived-at: Tue, 28 Nov 2017 23:58:02 -0000 This is an automated email from the ASF dual-hosted git repository. klund pushed a commit to branch feature/GEODE-3781 in repository https://gitbox.apache.org/repos/asf/geode.git commit 148399fb8de0f77293d7fa7a025b2d405c80f832 Author: Kirk Lund AuthorDate: Mon Nov 20 09:15:42 2017 -0800 GEODE-3824: add create jdbc-connection command * Fix XML parsing issues that prevented new command from functioning * Add missing support for extra database connection parameters --- .../{xml => }/ConnectionConfigBuilder.java | 33 ++++-- ...e.java => ConnectionConfigExistsException.java} | 20 ++-- .../jdbc/internal/ConnectionConfiguration.java | 37 ++++++- .../jdbc/internal/ConnectionManager.java | 2 +- .../internal/InternalJdbcConnectorService.java | 5 +- .../jdbc/internal/JdbcConnectorService.java | 10 +- .../jdbc/internal/cli/CreateConnectionCommand.java | 114 ++++++++++++++++++++ .../internal/cli/CreateConnectionFunction.java | 91 ++++++++++++++++ .../connectors/jdbc/internal/xml/ElementType.java | 14 ++- .../xml/JdbcConnectorServiceXmlGenerator.java | 23 +++- .../xml/JdbcConnectorServiceXmlParser.java | 4 +- .../internal/xml/JdbcServiceConfiguration.java | 2 +- .../jdbc/internal/xml/RegionMappingBuilder.java | 6 ++ .../geode.apache.org/schema/jdbc/jdbc-1.0.xsd | 85 +++++++++++++++ .../jdbc/internal/ConnectionConfigurationTest.java | 25 ++++- .../jdbc/internal/ConnectionManagerUnitTest.java | 2 +- .../jdbc/internal/JdbcConnectorServiceTest.java | 18 +++- .../jdbc/internal/TestConfigService.java | 4 +- .../cli/CreateConnectionCommandDUnitTest.java | 92 ++++++++++++++++ .../CreateConnectionCommandIntegrationTest.java | 92 ++++++++++++++++ .../internal/cli/CreateConnectionFunctionTest.java | 87 +++++++++++++++ .../internal/xml/ConnectionConfigBuilderTest.java | 6 +- .../jdbc/internal/xml/ElementTypeTest.java | 1 + ...onnectorServiceXmlGeneratorIntegrationTest.java | 117 +++++++++++++++++++-- .../JdbcConnectorServiceXmlIntegrationTest.java | 8 +- .../internal/xml/JdbcServiceConfigurationTest.java | 4 +- .../internal/configuration/domain/XmlEntity.java | 43 +++++++- 27 files changed, 885 insertions(+), 60 deletions(-) diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilder.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigBuilder.java similarity index 56% rename from geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilder.java rename to geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigBuilder.java index 34d61ad..c03034b 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilder.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigBuilder.java @@ -12,37 +12,52 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.apache.geode.connectors.jdbc.internal.xml; +package org.apache.geode.connectors.jdbc.internal; -import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PARAMS_DELIMITER; + +import java.util.HashMap; +import java.util.Map; + +public class ConnectionConfigBuilder { -class ConnectionConfigBuilder { private String name; private String url; private String user; private String password; + private Map parameters = new HashMap<>(); - ConnectionConfigBuilder withName(String name) { + public ConnectionConfigBuilder withName(String name) { this.name = name; return this; } - ConnectionConfigBuilder withUrl(String url) { + public ConnectionConfigBuilder withUrl(String url) { this.url = url; return this; } - ConnectionConfigBuilder withUser(String user) { + public ConnectionConfigBuilder withUser(String user) { this.user = user; return this; } - ConnectionConfigBuilder withPassword(String password) { + public ConnectionConfigBuilder withPassword(String password) { this.password = password; return this; } - ConnectionConfiguration build() { - return new ConnectionConfiguration(name, url, user, password); + public ConnectionConfigBuilder withParameters(String[] params) { + for (String param : params) { + String[] keyValuePair = param.split(PARAMS_DELIMITER); + if (keyValuePair.length == 2) { + parameters.put(keyValuePair[0], keyValuePair[1]); + } + } + return this; + } + + public ConnectionConfiguration build() { + return new ConnectionConfiguration(name, url, user, password, parameters); } } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigExistsException.java similarity index 63% copy from geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java copy to geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigExistsException.java index d0bd167..d960505 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigExistsException.java @@ -14,17 +14,17 @@ */ package org.apache.geode.connectors.jdbc.internal; -import org.apache.geode.cache.Cache; -import org.apache.geode.internal.cache.CacheService; -import org.apache.geode.internal.cache.extension.Extension; +public class ConnectionConfigExistsException extends RuntimeException { -public interface InternalJdbcConnectorService extends Extension, CacheService { - void addOrUpdateConnectionConfig(ConnectionConfiguration config); + public ConnectionConfigExistsException() { + super(); + } - void addOrUpdateRegionMapping(RegionMapping mapping); - - ConnectionConfiguration getConnectionConfig(String connectionName); - - RegionMapping getMappingForRegion(String regionName); + public ConnectionConfigExistsException(String message) { + super(message); + } + public ConnectionConfigExistsException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfiguration.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfiguration.java index fa3807d..72b4aa8 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfiguration.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfiguration.java @@ -14,18 +14,30 @@ */ package org.apache.geode.connectors.jdbc.internal; -public class ConnectionConfiguration { +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class ConnectionConfiguration implements Serializable { + private static final Object USER = "user"; + private static final Object PASSWORD = "password"; private final String name; private final String url; private final String user; private final String password; + private final Map parameters; - public ConnectionConfiguration(String name, String url, String user, String password) { + public ConnectionConfiguration(String name, String url, String user, String password, + Map parameters) { this.name = name; this.url = url; this.user = user; this.password = password; + this.parameters = + parameters == null ? new HashMap<>() : Collections.unmodifiableMap(parameters); } public String getName() { @@ -44,6 +56,19 @@ public class ConnectionConfiguration { return password; } + public Properties getConnectionProperties() { + Properties properties = new Properties(); + + properties.putAll(parameters); + if (user != null) { + properties.put(USER, user); + } + if (password != null) { + properties.put(PASSWORD, password); + } + return properties; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -64,7 +89,10 @@ public class ConnectionConfiguration { if (user != null ? !user.equals(that.user) : that.user != null) { return false; } - return password != null ? password.equals(that.password) : that.password == null; + if (password != null ? !password.equals(that.password) : that.password != null) { + return false; + } + return parameters != null ? parameters.equals(that.parameters) : that.parameters == null; } @Override @@ -73,12 +101,13 @@ public class ConnectionConfiguration { result = 31 * result + (url != null ? url.hashCode() : 0); result = 31 * result + (user != null ? user.hashCode() : 0); result = 31 * result + (password != null ? password.hashCode() : 0); + result = 31 * result + (parameters != null ? parameters.hashCode() : 0); return result; } @Override public String toString() { return "ConnectionConfiguration{" + "name='" + name + '\'' + ", url='" + url + '\'' + ", user='" - + user + '\'' + ", password='" + password + '\'' + '}'; + + user + '\'' + ", password='" + password + '\'' + ", parameters=" + parameters + '}'; } } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionManager.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionManager.java index d382112..30d030f 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionManager.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/ConnectionManager.java @@ -99,7 +99,7 @@ class ConnectionManager { // package protected for testing purposes only Connection getSQLConnection(ConnectionConfiguration config) throws SQLException { - return DriverManager.getConnection(config.getUrl(), config.getUser(), config.getPassword()); + return DriverManager.getConnection(config.getUrl(), config.getConnectionProperties()); } private synchronized Connection getNewConnection(ConnectionConfiguration config) { diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java index d0bd167..882dd20 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/InternalJdbcConnectorService.java @@ -19,12 +19,13 @@ import org.apache.geode.internal.cache.CacheService; import org.apache.geode.internal.cache.extension.Extension; public interface InternalJdbcConnectorService extends Extension, CacheService { - void addOrUpdateConnectionConfig(ConnectionConfiguration config); + + void createConnectionConfig(ConnectionConfiguration config) + throws ConnectionConfigExistsException; void addOrUpdateRegionMapping(RegionMapping mapping); ConnectionConfiguration getConnectionConfig(String connectionName); RegionMapping getMappingForRegion(String regionName); - } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorService.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorService.java index 9af7aeb..222345f 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorService.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorService.java @@ -32,18 +32,24 @@ public class JdbcConnectorService implements InternalJdbcConnectorService { private volatile InternalCache cache; private boolean registered; + @Override public ConnectionConfiguration getConnectionConfig(String connectionName) { return connectionsByName.get(connectionName); } + @Override public RegionMapping getMappingForRegion(String regionName) { return mappingsByRegion.get(regionName); } @Override - public void addOrUpdateConnectionConfig(ConnectionConfiguration config) { + public void createConnectionConfig(ConnectionConfiguration config) { registerAsExtension(); - connectionsByName.put(config.getName(), config); + ConnectionConfiguration existing = connectionsByName.putIfAbsent(config.getName(), config); + if (existing != null) { + throw new ConnectionConfigExistsException( + "ConnectionConfiguration " + config.getName() + " exists"); + } } @Override diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java new file mode 100644 index 0000000..a3f676f --- /dev/null +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommand.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.connectors.jdbc.internal.cli; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.logging.log4j.Logger; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; + +import org.apache.geode.cache.execute.ResultCollector; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.management.cli.CliMetaData; +import org.apache.geode.management.cli.Result; +import org.apache.geode.management.internal.cli.commands.GfshCommand; +import org.apache.geode.management.internal.cli.functions.CliFunctionResult; +import org.apache.geode.management.internal.cli.i18n.CliStrings; +import org.apache.geode.management.internal.cli.result.ResultBuilder; +import org.apache.geode.management.internal.cli.result.TabularResultData; +import org.apache.geode.management.internal.configuration.domain.XmlEntity; +import org.apache.geode.management.internal.security.ResourceOperation; +import org.apache.geode.security.ResourcePermission; + +public class CreateConnectionCommand implements GfshCommand { + private static final Logger logger = LogService.getLogger(); + + static final String CREATE_CONNECTION = "create jdbc-connection"; + static final String CREATE_CONNECTION__HELP = "Create JDBC connection for JDBC Connector."; + static final String CREATE_CONNECTION__NAME = "name"; + static final String CREATE_CONNECTION__NAME__HELP = "Name of the JDBC connection to be created."; + static final String CREATE_CONNECTION__URL = "url"; + static final String CREATE_CONNECTION__URL__HELP = "URL location for the database"; + static final String CREATE_CONNECTION__USER = "user"; + static final String CREATE_CONNECTION__USER__HELP = + "Name of user to use when connecting to the database"; + static final String CREATE_CONNECTION__PASSWORD = "password"; + static final String CREATE_CONNECTION__PASSWORD__HELP = + "Password of user to use when connecting to the database"; + static final String CREATE_CONNECTION__PARAMS = "params"; + static final String CREATE_CONNECTION__PARAMS__HELP = + "Comma delimited list of additional parameters to use when connecting to the database"; + + private static final String ERROR_PREFIX = "ERROR: "; + + @CliCommand(value = CREATE_CONNECTION, help = CREATE_CONNECTION__HELP) + @CliMetaData(relatedTopic = CliStrings.TOPIC_GEODE_REGION) + @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER, + operation = ResourcePermission.Operation.MANAGE) + public Result createConnection( + @CliOption(key = CREATE_CONNECTION__NAME, mandatory = true, + help = CREATE_CONNECTION__NAME__HELP) String name, + @CliOption(key = CREATE_CONNECTION__URL, mandatory = true, + help = CREATE_CONNECTION__URL__HELP) String url, + @CliOption(key = CREATE_CONNECTION__USER, help = CREATE_CONNECTION__USER__HELP) String user, + @CliOption(key = CREATE_CONNECTION__PASSWORD, + help = CREATE_CONNECTION__PASSWORD__HELP) String password, + @CliOption(key = CREATE_CONNECTION__PARAMS, + help = CREATE_CONNECTION__PARAMS__HELP) String[] params) { + + Set membersToCreateConnectionOn = getMembers(null, null); + + ConnectionConfigBuilder builder = new ConnectionConfigBuilder().withName(name).withUrl(url) + .withUser(user).withPassword(password).withParameters(params); + ConnectionConfiguration configuration = builder.build(); + + ResultCollector resultCollector = + executeFunction(new CreateConnectionFunction(), configuration, membersToCreateConnectionOn); + + Object resultCollectorResult = resultCollector.getResult(); + + List regionCreateResults = (List) resultCollectorResult; + + AtomicReference xmlEntity = new AtomicReference<>(); + TabularResultData tabularResultData = ResultBuilder.createTabularResultData(); + for (CliFunctionResult regionCreateResult : regionCreateResults) { + boolean success = regionCreateResult.isSuccessful(); + tabularResultData.accumulate("Member", regionCreateResult.getMemberIdOrName()); + tabularResultData.accumulate("Status", + (success ? "" : ERROR_PREFIX) + regionCreateResult.getMessage()); + + if (success) { + xmlEntity.set(regionCreateResult.getXmlEntity()); + } else { + tabularResultData.setStatus(Result.Status.ERROR); + } + } + + Result result = ResultBuilder.buildResult(tabularResultData); + + if (xmlEntity.get() != null) { + persistClusterConfiguration(result, + () -> getSharedConfiguration().addXmlEntity(xmlEntity.get(), null)); + } + + return result; + } +} diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunction.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunction.java new file mode 100644 index 0000000..649ba25 --- /dev/null +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunction.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.connectors.jdbc.internal.cli; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.cache.execute.Function; +import org.apache.geode.cache.execute.FunctionContext; +import org.apache.geode.cache.execute.ResultSender; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigExistsException; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; +import org.apache.geode.connectors.jdbc.internal.xml.ElementType; +import org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlGenerator; +import org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser; +import org.apache.geode.internal.InternalEntity; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.internal.cache.xmlcache.CacheXml; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.management.internal.cli.CliUtil; +import org.apache.geode.management.internal.cli.functions.CliFunctionResult; +import org.apache.geode.management.internal.configuration.domain.XmlEntity; + +public class CreateConnectionFunction implements Function, InternalEntity { + private static final Logger logger = LogService.getLogger(); + + private static final String ID = CreateConnectionFunction.class.getName(); + + @Override + public boolean isHA() { + return false; + } + + @Override + public String getId() { + return ID; + } + + @Override + public void execute(FunctionContext context) { + ResultSender resultSender = context.getResultSender(); + + InternalCache cache = (InternalCache) context.getCache(); + String memberNameOrId = + CliUtil.getMemberNameOrId(cache.getDistributedSystem().getDistributedMember()); + + ConnectionConfiguration configuration = context.getArguments(); + + try { + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + service.createConnectionConfig(configuration); + + XmlEntity xmlEntity = new XmlEntity(CacheXml.CACHE, JdbcConnectorServiceXmlGenerator.PREFIX, + JdbcConnectorServiceXmlParser.NAMESPACE, ElementType.CONNECTION_SERVICE.getTypeName()); + + resultSender.lastResult(new CliFunctionResult(memberNameOrId, xmlEntity, + "Created JDBC connection " + configuration.getName() + " on " + memberNameOrId)); + + } catch (Exception e) { + String exceptionMsg = e.getMessage(); + if (exceptionMsg == null) { + exceptionMsg = CliUtil.stackTraceAsString(e); + } + resultSender.lastResult(handleException(memberNameOrId, exceptionMsg, e)); + } + } + + private CliFunctionResult handleException(final String memberNameOrId, final String exceptionMsg, + final Exception e) { + if (e != null && logger.isDebugEnabled()) { + logger.debug(e.getMessage(), e); + } + if (exceptionMsg != null) { + return new CliFunctionResult(memberNameOrId, false, exceptionMsg); + } + + return new CliFunctionResult(memberNameOrId); + } +} diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ElementType.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ElementType.java index 580910c..abfcc48 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ElementType.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/ElementType.java @@ -19,11 +19,12 @@ import java.util.Stack; import org.xml.sax.Attributes; import org.apache.geode.cache.CacheXmlException; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; import org.apache.geode.connectors.jdbc.internal.RegionMapping; import org.apache.geode.internal.cache.xmlcache.CacheCreation; -enum ElementType { +public enum ElementType { CONNECTION_SERVICE("connector-service") { @Override void startElement(Stack stack, Attributes attributes) { @@ -54,9 +55,18 @@ enum ElementType { .withUrl(attributes.getValue(JdbcConnectorServiceXmlParser.URL)) .withUser(attributes.getValue(JdbcConnectorServiceXmlParser.USER)) .withPassword(attributes.getValue(JdbcConnectorServiceXmlParser.PASSWORD)); + addParameters(connectionConfig, + attributes.getValue(JdbcConnectorServiceXmlParser.PARAMETERS)); stack.push(connectionConfig); } + private void addParameters(ConnectionConfigBuilder connectionConfig, String value) { + if (value == null) { + return; + } + connectionConfig.withParameters(value.split(",")); + } + @Override void endElement(Stack stack) { ConnectionConfiguration config = ((ConnectionConfigBuilder) stack.pop()).build(); @@ -120,7 +130,7 @@ enum ElementType { throw new IllegalArgumentException("Invalid type '" + typeName + "'"); } - String getTypeName() { + public String getTypeName() { return typeName; } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGenerator.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGenerator.java index 867dd7f..afc929b 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGenerator.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGenerator.java @@ -19,6 +19,8 @@ import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorService import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.FIELD_NAME; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.NAME; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.NAMESPACE; +import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PARAMETERS; +import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PARAMS_DELIMITER; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PASSWORD; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PDX_CLASS; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.PRIMARY_KEY_IN_VALUE; @@ -30,6 +32,7 @@ import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorService import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Properties; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; @@ -43,8 +46,10 @@ import org.apache.geode.internal.cache.xmlcache.XmlGenerator; import org.apache.geode.internal.cache.xmlcache.XmlGeneratorUtils; public class JdbcConnectorServiceXmlGenerator implements XmlGenerator { + + public static final String PREFIX = "jdbc"; + private static final AttributesImpl EMPTY = new AttributesImpl(); - static final String PREFIX = "jdbc"; private final Collection connections; private final Collection mappings; @@ -97,6 +102,7 @@ public class JdbcConnectorServiceXmlGenerator implements XmlGenerator { XmlGeneratorUtils.addAttribute(attributes, URL, config.getUrl()); XmlGeneratorUtils.addAttribute(attributes, USER, config.getUser()); XmlGeneratorUtils.addAttribute(attributes, PASSWORD, config.getPassword()); + XmlGeneratorUtils.addAttribute(attributes, PARAMETERS, createParametersString(config)); XmlGeneratorUtils.emptyElement(handler, PREFIX, ElementType.CONNECTION.getTypeName(), attributes); } @@ -127,4 +133,19 @@ public class JdbcConnectorServiceXmlGenerator implements XmlGenerator { fieldAttributes); } } + + private String createParametersString(ConnectionConfiguration config) { + Properties properties = config.getConnectionProperties(); + StringBuilder stringBuilder = new StringBuilder(); + for (Map.Entry entry : properties.entrySet()) { + Object key = entry.getKey(); + if (!key.equals(USER) && !key.equals(PASSWORD)) { + if (stringBuilder.length() > 0) { + stringBuilder.append(","); + } + stringBuilder.append(key).append(PARAMS_DELIMITER).append(entry.getValue()); + } + } + return stringBuilder.toString(); + } } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlParser.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlParser.java index 7caaa1d..7fdd691 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlParser.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlParser.java @@ -20,11 +20,13 @@ import org.xml.sax.SAXException; import org.apache.geode.internal.cache.xmlcache.AbstractXmlParser; public class JdbcConnectorServiceXmlParser extends AbstractXmlParser { - static final String NAMESPACE = "http://geode.apache.org/schema/jdbc-connector"; + public static final String NAMESPACE = "http://geode.apache.org/schema/jdbc"; + public static final String PARAMS_DELIMITER = ":"; static final String NAME = "name"; static final String URL = "url"; static final String USER = "user"; static final String PASSWORD = "password"; + static final String PARAMETERS = "parameters"; static final String REGION = "region"; static final String CONNECTION_NAME = "connection-name"; static final String TABLE = "table"; diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfiguration.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfiguration.java index 2892334..4c19b2b 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfiguration.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfiguration.java @@ -54,7 +54,7 @@ public class JdbcServiceConfiguration implements Extension { InternalCache internalCache = (InternalCache) target; InternalJdbcConnectorService service = internalCache.getService(InternalJdbcConnectorService.class); - connections.forEach(connection -> service.addOrUpdateConnectionConfig(connection)); + connections.forEach(connection -> service.createConnectionConfig(connection)); mappings.forEach(mapping -> service.addOrUpdateRegionMapping(mapping)); } } diff --git a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/RegionMappingBuilder.java b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/RegionMappingBuilder.java index b4adcaf..48c8543 100644 --- a/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/RegionMappingBuilder.java +++ b/geode-connectors/src/main/java/org/apache/geode/connectors/jdbc/internal/xml/RegionMappingBuilder.java @@ -48,11 +48,17 @@ class RegionMappingBuilder { return this; } + // TODO: delete withPrimaryKeyInValue(String) RegionMappingBuilder withPrimaryKeyInValue(String primaryKeyInValue) { this.primaryKeyInValue = Boolean.parseBoolean(primaryKeyInValue); return this; } + RegionMappingBuilder withPrimaryKeyInValue(boolean primaryKeyInValue) { + this.primaryKeyInValue = primaryKeyInValue; + return this; + } + RegionMappingBuilder withFieldToColumnMapping(String fieldName, String columnMapping) { this.fieldToColumnMap.put(fieldName, columnMapping); return this; diff --git a/geode-connectors/src/main/resources/META-INF/services/schemas/geode.apache.org/schema/jdbc/jdbc-1.0.xsd b/geode-connectors/src/main/resources/META-INF/services/schemas/geode.apache.org/schema/jdbc/jdbc-1.0.xsd new file mode 100644 index 0000000..1c913dd --- /dev/null +++ b/geode-connectors/src/main/resources/META-INF/services/schemas/geode.apache.org/schema/jdbc/jdbc-1.0.xsd @@ -0,0 +1,85 @@ + + + + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigurationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigurationTest.java index 639e278..ba55bda 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigurationTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionConfigurationTest.java @@ -16,6 +16,9 @@ package org.apache.geode.connectors.jdbc.internal; import static org.assertj.core.api.Assertions.assertThat; +import java.util.HashMap; +import java.util.Map; + import org.junit.Test; import org.junit.experimental.categories.Category; @@ -26,7 +29,7 @@ public class ConnectionConfigurationTest { @Test public void initiatedWithNullValues() { - ConnectionConfiguration config = new ConnectionConfiguration(null, null, null, null); + ConnectionConfiguration config = new ConnectionConfiguration(null, null, null, null, null); assertThat(config.getName()).isNull(); assertThat(config.getUrl()).isNull(); assertThat(config.getUser()).isNull(); @@ -36,28 +39,40 @@ public class ConnectionConfigurationTest { @Test public void hasCorrectName() { String name = "name"; - ConnectionConfiguration config = new ConnectionConfiguration(name, null, null, null); + ConnectionConfiguration config = new ConnectionConfiguration(name, null, null, null, null); assertThat(config.getName()).isEqualTo(name); } @Test public void hasCorrectUrl() { String url = "url"; - ConnectionConfiguration config = new ConnectionConfiguration(null, url, null, null); + ConnectionConfiguration config = new ConnectionConfiguration(null, url, null, null, null); assertThat(config.getUrl()).isEqualTo(url); } @Test public void hasCorrectUser() { String user = "user"; - ConnectionConfiguration config = new ConnectionConfiguration(null, null, user, null); + ConnectionConfiguration config = new ConnectionConfiguration(null, null, user, null, null); assertThat(config.getUser()).isEqualTo(user); } @Test public void hasCorrectPassword() { String password = "password"; - ConnectionConfiguration config = new ConnectionConfiguration(null, null, null, password); + ConnectionConfiguration config = new ConnectionConfiguration(null, null, null, password, null); assertThat(config.getPassword()).isEqualTo(password); } + + @Test + public void hasCorrectProperties() { + Map parameters = new HashMap<>(); + parameters.put("param1", "value1"); + parameters.put("param2", "value2"); + ConnectionConfiguration config = + new ConnectionConfiguration(null, null, "username", "secret", parameters); + assertThat(config.getConnectionProperties()).containsEntry("user", "username") + .containsEntry("password", "secret").containsEntry("param1", "value1") + .containsEntry("param2", "value2"); + } } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionManagerUnitTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionManagerUnitTest.java index 8358d1c..b6b4a67 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionManagerUnitTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/ConnectionManagerUnitTest.java @@ -233,7 +233,7 @@ public class ConnectionManagerUnitTest { private ConnectionConfiguration getTestConnectionConfig(String name, String url, String user, String password) { - ConnectionConfiguration config = new ConnectionConfiguration(name, url, user, password); + ConnectionConfiguration config = new ConnectionConfiguration(name, url, user, password, null); return config; } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorServiceTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorServiceTest.java index d91e4df..d32af99 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorServiceTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/JdbcConnectorServiceTest.java @@ -15,6 +15,7 @@ package org.apache.geode.connectors.jdbc.internal; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,7 +55,7 @@ public class JdbcConnectorServiceTest { public void returnsCorrectConfig() { ConnectionConfiguration config = mock(ConnectionConfiguration.class); when(config.getName()).thenReturn(TEST_CONFIG_NAME); - service.addOrUpdateConnectionConfig(config); + service.createConnectionConfig(config); assertThat(service.getConnectionConfig(TEST_CONFIG_NAME)).isSameAs(config); } @@ -63,7 +64,7 @@ public class JdbcConnectorServiceTest { public void doesNotReturnConfigWithDifferentName() { ConnectionConfiguration config = mock(ConnectionConfiguration.class); when(config.getName()).thenReturn("theOtherConfig"); - service.addOrUpdateConnectionConfig(config); + service.createConnectionConfig(config); assertThat(service.getConnectionConfig(TEST_CONFIG_NAME)).isNull(); } @@ -85,4 +86,17 @@ public class JdbcConnectorServiceTest { assertThat(service.getMappingForRegion(TEST_REGION_NAME)).isNull(); } + + @Test + public void createConnectionConfig_throwsIfConnectionExists() { + ConnectionConfiguration config = mock(ConnectionConfiguration.class); + when(config.getName()).thenReturn(TEST_CONFIG_NAME); + service.createConnectionConfig(config); + + ConnectionConfiguration config2 = mock(ConnectionConfiguration.class); + when(config2.getName()).thenReturn(TEST_CONFIG_NAME); + + assertThatThrownBy(() -> service.createConnectionConfig(config2)) + .isInstanceOf(ConnectionConfigExistsException.class).hasMessageContaining(TEST_CONFIG_NAME); + } } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/TestConfigService.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/TestConfigService.java index b387d72..ec5c668 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/TestConfigService.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/TestConfigService.java @@ -38,7 +38,7 @@ public class TestConfigService { JdbcConnectorService service = new JdbcConnectorService(); service.init(cache); - service.addOrUpdateConnectionConfig(createConnectionConfig()); + service.createConnectionConfig(createConnectionConfig()); service.addOrUpdateRegionMapping(createRegionMapping()); return service; } @@ -49,6 +49,6 @@ public class TestConfigService { } private static ConnectionConfiguration createConnectionConfig() { - return new ConnectionConfiguration(CONNECTION_CONFIG_NAME, CONNECTION_URL, null, null); + return new ConnectionConfiguration(CONNECTION_CONFIG_NAME, CONNECTION_URL, null, null, null); } } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandDUnitTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandDUnitTest.java new file mode 100644 index 0000000..e1a97fd --- /dev/null +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandDUnitTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.connectors.jdbc.internal.cli; + +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION; +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION__NAME; +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION__PARAMS; +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION__PASSWORD; +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION__URL; +import static org.apache.geode.connectors.jdbc.internal.cli.CreateConnectionCommand.CREATE_CONNECTION__USER; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; +import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.management.internal.cli.util.CommandStringBuilder; +import org.apache.geode.test.dunit.rules.LocatorServerStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; +import org.apache.geode.test.junit.categories.DistributedTest; +import org.apache.geode.test.junit.rules.GfshCommandRule; +import org.apache.geode.test.junit.rules.serializable.SerializableTestName; + +@Category(DistributedTest.class) +public class CreateConnectionCommandDUnitTest { + + @Rule + public transient GfshCommandRule gfsh = new GfshCommandRule(); + + @Rule + public LocatorServerStartupRule startupRule = new LocatorServerStartupRule(); + + @Rule + public SerializableTestName testName = new SerializableTestName(); + + private MemberVM locator; + private MemberVM server; + + @Before + public void before() throws Exception { + locator = startupRule.startLocatorVM(0); + server = startupRule.startServerVM(1, locator.getPort()); + + gfsh.connectAndVerify(locator); + } + + @Test + public void createsConnection() throws Exception { + CommandStringBuilder csb = new CommandStringBuilder(CREATE_CONNECTION); + csb.addOption(CREATE_CONNECTION__NAME, "name"); + csb.addOption(CREATE_CONNECTION__URL, "url"); + csb.addOption(CREATE_CONNECTION__USER, "username"); + csb.addOption(CREATE_CONNECTION__PASSWORD, "secret"); + csb.addOption(CREATE_CONNECTION__PARAMS, "param1:value1,param2:value2"); + + gfsh.executeAndAssertThat(csb.toString()).statusIsSuccess(); + + locator.invoke(() -> { + String xml = InternalLocator.getLocator().getSharedConfiguration().getConfiguration("cluster") + .getCacheXmlContent(); + assertThat(xml).contains("jdbc:connector-service"); + }); + + server.invoke(() -> { + InternalCache cache = LocatorServerStartupRule.getCache(); + ConnectionConfiguration config = + cache.getService(InternalJdbcConnectorService.class).getConnectionConfig("name"); + assertThat(config.getUrl()).isEqualTo("url"); + assertThat(config.getUser()).isEqualTo("username"); + assertThat(config.getPassword()).isEqualTo("secret"); + assertThat(config.getConnectionProperties()).containsEntry("param1", "value1") + .containsEntry("param2", "value2"); + }); + } +} diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java new file mode 100644 index 0000000..e6ea066 --- /dev/null +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionCommandIntegrationTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.connectors.jdbc.internal.cli; + +import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.management.cli.Result; +import org.apache.geode.management.internal.cli.result.CommandResult; +import org.apache.geode.test.junit.categories.IntegrationTest; + +@Category(IntegrationTest.class) +public class CreateConnectionCommandIntegrationTest { + + private InternalCache cache; + private CreateConnectionCommand createConnectionCommand; + + private String name; + private String url; + private String user; + private String password; + private String[] params; + + @Before + public void setup() throws Exception { + cache = (InternalCache) new CacheFactory().set(ENABLE_CLUSTER_CONFIGURATION, "true").create(); + createConnectionCommand = new CreateConnectionCommand(); + + name = "name"; + url = "url"; + user = "user"; + password = "password"; + params = new String[] {"param1:value1", "param2:value2"}; + } + + @After + public void tearDown() { + cache.close(); + } + + @Test + public void createsConnectionConfigurationInService() throws Exception { + Result result = createConnectionCommand.createConnection(name, url, user, password, params); + + assertThat(result.getStatus()).isSameAs(Result.Status.OK); + + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + ConnectionConfiguration connectionConfig = service.getConnectionConfig(name); + + assertThat(connectionConfig).isNotNull(); + assertThat(connectionConfig.getName()).isEqualTo(name); + assertThat(connectionConfig.getUrl()).isEqualTo(url); + assertThat(connectionConfig.getUser()).isEqualTo(user); + assertThat(connectionConfig.getPassword()).isEqualTo(password); + assertThat(connectionConfig.getConnectionProperties()).containsEntry("param1", "value1") + .containsEntry("param2", "value2"); + } + + @Test + public void createsConnectionOnceOnly() throws Exception { + createConnectionCommand.createConnection(name, url, user, password, params); + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + + ConnectionConfiguration connectionConfig = service.getConnectionConfig(name); + + Result result = createConnectionCommand.createConnection(name, url, user, password, params); + assertThat(((CommandResult) result).toJson()).contains("ERROR"); + + assertThat(service.getConnectionConfig(name)).isSameAs(connectionConfig); + } +} diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunctionTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunctionTest.java new file mode 100644 index 0000000..7452d88 --- /dev/null +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/cli/CreateConnectionFunctionTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You 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.apache.geode.connectors.jdbc.internal.cli; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.cache.execute.FunctionContext; +import org.apache.geode.cache.execute.ResultSender; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; +import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; +import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.distributed.DistributedSystem; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.test.junit.categories.UnitTest; + +@Category(UnitTest.class) +public class CreateConnectionFunctionTest { + + private CreateConnectionFunction function; + private FunctionContext context; + private ResultSender resultSender; + private InternalCache cache; + private DistributedSystem system; + private DistributedMember member; + private ConnectionConfiguration configuration; + private InternalJdbcConnectorService service; + + @Before + public void setup() { + configuration = new ConnectionConfigBuilder().build(); + + context = mock(FunctionContext.class); + resultSender = mock(ResultSender.class); + cache = mock(InternalCache.class); + system = mock(DistributedSystem.class); + member = mock(DistributedMember.class); + service = mock(InternalJdbcConnectorService.class); + + when(context.getResultSender()).thenReturn(resultSender); + when(context.getCache()).thenReturn(cache); + when(cache.getDistributedSystem()).thenReturn(system); + when(system.getDistributedMember()).thenReturn(member); + when(context.getArguments()).thenReturn(configuration); + when(cache.getService(eq(InternalJdbcConnectorService.class))).thenReturn(service); + + function = new CreateConnectionFunction(); + } + + @Test + public void isHAIsFalse() throws Exception { + assertThat(function.isHA()).isFalse(); + } + + @Test + public void getId() throws Exception { + assertThat(function.getId()).isEqualTo(function.getClass().getName()); + } + + @Test + public void execute() throws Exception { + function.execute(context); + verify(service, times(1)).createConnectionConfig(configuration); + } + +} diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilderTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilderTest.java index e97ecc1..ffc8abc 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilderTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ConnectionConfigBuilderTest.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; import org.apache.geode.test.junit.categories.UnitTest; @@ -36,11 +37,14 @@ public class ConnectionConfigBuilderTest { @Test public void createsObjectWithCorrectValues() { ConnectionConfigBuilder builder = new ConnectionConfigBuilder(); - builder.withName("name").withUrl("url").withUser("user").withPassword("password"); + builder.withName("name").withUrl("url").withUser("user").withPassword("password") + .withParameters(new String[] {"param1:value1", "param2:value2"}); ConnectionConfiguration config = builder.build(); assertThat(config.getName()).isEqualTo("name"); assertThat(config.getUrl()).isEqualTo("url"); assertThat(config.getUser()).isEqualTo("user"); assertThat(config.getPassword()).isEqualTo("password"); + assertThat(config.getConnectionProperties()).containsEntry("param1", "value1") + .containsEntry("param2", "value2"); } } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ElementTypeTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ElementTypeTest.java index 8c290b7..4877ac9 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ElementTypeTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/ElementTypeTest.java @@ -43,6 +43,7 @@ import org.xml.sax.Attributes; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheXmlException; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; import org.apache.geode.connectors.jdbc.internal.RegionMapping; import org.apache.geode.internal.cache.extension.ExtensionPoint; diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGeneratorIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGeneratorIntegrationTest.java index 5b6d260..6280d6f 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGeneratorIntegrationTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlGeneratorIntegrationTest.java @@ -27,6 +27,7 @@ import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorService import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.TABLE; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.URL; import static org.apache.geode.connectors.jdbc.internal.xml.JdbcConnectorServiceXmlParser.USER; +import static org.apache.geode.distributed.ConfigurationProperties.CACHE_XML_FILE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -34,10 +35,14 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; import org.junit.After; import org.junit.Before; @@ -51,29 +56,36 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.apache.geode.cache.CacheFactory; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; import org.apache.geode.connectors.jdbc.internal.RegionMapping; import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.internal.cache.xmlcache.CacheXml; import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator; +import org.apache.geode.internal.cache.xmlcache.CacheXmlParser; +import org.apache.geode.management.internal.configuration.utils.XmlUtils; import org.apache.geode.test.junit.categories.IntegrationTest; @Category(IntegrationTest.class) public class JdbcConnectorServiceXmlGeneratorIntegrationTest { private InternalCache cache; + private File cacheXmlFile; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before - public void setup() { + public void setup() throws IOException { cache = (InternalCache) new CacheFactory().create(); + cacheXmlFile = temporaryFolder.newFile("cache.xml"); } @After public void tearDown() { cache.close(); + cache = null; } @Test @@ -95,7 +107,7 @@ public class JdbcConnectorServiceXmlGeneratorIntegrationTest { InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); ConnectionConfiguration config = new ConnectionConfigBuilder().withName("name").withUrl("url") .withUser("username").withPassword("secret").build(); - service.addOrUpdateConnectionConfig(config); + service.createConnectionConfig(config); generateXml(); @@ -149,6 +161,98 @@ public class JdbcConnectorServiceXmlGeneratorIntegrationTest { validatePresenceOfFieldMapping(fieldMappingElements, "fieldName2", "columnMapping2"); } + @Test + public void generatedXmlWithConnectionConfigurationCanBeParsed() throws Exception { + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + ConnectionConfiguration config = new ConnectionConfigBuilder().withName("name").withUrl("url") + .withUser("username").withPassword("secret").build(); + service.createConnectionConfig(config); + generateXml(); + + cache.close(); + cache = (InternalCache) new CacheFactory().set(CACHE_XML_FILE, cacheXmlFile.getAbsolutePath()) + .create(); + + service = cache.getService(InternalJdbcConnectorService.class); + + assertThat(service.getConnectionConfig("name")).isEqualTo(config); + } + + @Test + public void generatedXmlWithConnectionConfigurationCanBeXPathed() throws Exception { + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + ConnectionConfiguration config = new ConnectionConfigBuilder().withName("name").withUrl("url") + .withUser("username").withPassword("secret").build(); + service.createConnectionConfig(config); + generateXml(); + + for (String line : Files.readAllLines(cacheXmlFile.toPath())) { + System.out.println(line); + } + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.setEntityResolver(new CacheXmlParser()); + + Document document = builder.parse(cacheXmlFile); + System.out.println(document.getDocumentElement()); + XmlUtils.XPathContext xpathContext = new XmlUtils.XPathContext(); + xpathContext.addNamespace(CacheXml.PREFIX, CacheXml.GEODE_NAMESPACE); + xpathContext.addNamespace(PREFIX, NAMESPACE); // TODO: wrap this line with conditional + // Create an XPathContext here + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(xpathContext); + Object result = xpath.evaluate("//cache/jdbc:connector-service", document, XPathConstants.NODE); + // Node element = XmlUtils.querySingleElement(document, "//cache/jdbc:connector-service", + // xpathContext); + // Must copy to preserve namespaces. + System.out.println("RESULT = " + XmlUtils.elementToString((Element) result)); + } + + @Test + public void generatedXmlWithRegionMappingCanBeParsed() throws Exception { + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + RegionMapping mapping = new RegionMappingBuilder().withRegionName("region") + .withPdxClassName("class").withTableName("table").withConnectionConfigName("connection") + .withPrimaryKeyInValue(true).withFieldToColumnMapping("field1", "columnMapping1") + .withFieldToColumnMapping("field2", "columnMapping2").build(); + service.addOrUpdateRegionMapping(mapping); + generateXml(); + + cache.close(); + cache = (InternalCache) new CacheFactory().set(CACHE_XML_FILE, cacheXmlFile.getAbsolutePath()) + .create(); + + service = cache.getService(InternalJdbcConnectorService.class); + + assertThat(service.getMappingForRegion("region")).isEqualTo(mapping); + } + + @Test + public void generatedXmlWithEverythingCanBeParsed() throws Exception { + InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); + ConnectionConfiguration config = new ConnectionConfigBuilder().withName("name").withUrl("url") + .withUser("username").withPassword("secret").build(); + service.createConnectionConfig(config); + RegionMapping mapping = new RegionMappingBuilder().withRegionName("region") + .withPdxClassName("class").withTableName("table").withConnectionConfigName("connection") + .withPrimaryKeyInValue(true).withFieldToColumnMapping("field1", "columnMapping1") + .withFieldToColumnMapping("field2", "columnMapping2").build(); + service.addOrUpdateRegionMapping(mapping); + generateXml(); + + cache.close(); + cache = (InternalCache) new CacheFactory().set(CACHE_XML_FILE, cacheXmlFile.getAbsolutePath()) + .create(); + + service = cache.getService(InternalJdbcConnectorService.class); + + assertThat(service.getConnectionConfig("name")).isEqualTo(config); + assertThat(service.getMappingForRegion("region")).isEqualTo(mapping); + } + private void validatePresenceOfFieldMapping(NodeList elements, String fieldName, String columnName) { for (int i = 0; i < elements.getLength(); i++) { @@ -177,19 +281,18 @@ public class JdbcConnectorServiceXmlGeneratorIntegrationTest { private Document getCacheXmlDocument() throws IOException, SAXException, ParserConfigurationException { - File cacheXml = new File(temporaryFolder.getRoot(), "cache.xml"); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + dbFactory.setNamespaceAware(false); + dbFactory.setValidating(false); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - Document document = dBuilder.parse(cacheXml); + Document document = dBuilder.parse(cacheXmlFile); document.getDocumentElement().normalize(); return document; } private void generateXml() throws IOException { - File cacheXml = new File(temporaryFolder.getRoot(), "cache.xml"); - PrintWriter printWriter = new PrintWriter(new FileWriter(cacheXml)); + PrintWriter printWriter = new PrintWriter(new FileWriter(cacheXmlFile)); CacheXmlGenerator.generate(cache, printWriter, true, false, false); printWriter.flush(); } - } diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlIntegrationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlIntegrationTest.java index ac7fcf5..48fd9a8 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlIntegrationTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcConnectorServiceXmlIntegrationTest.java @@ -30,6 +30,7 @@ import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; import org.apache.geode.cache.CacheFactory; +import org.apache.geode.connectors.jdbc.internal.ConnectionConfigBuilder; import org.apache.geode.connectors.jdbc.internal.ConnectionConfiguration; import org.apache.geode.connectors.jdbc.internal.InternalJdbcConnectorService; import org.apache.geode.connectors.jdbc.internal.JdbcConnectorService; @@ -82,11 +83,12 @@ public class JdbcConnectorServiceXmlIntegrationTest { private void configureService() { InternalJdbcConnectorService service = cache.getService(InternalJdbcConnectorService.class); config1 = new ConnectionConfigBuilder().withName("connection1").withUrl("url1") - .withUser("username1").withPassword("secret1").build(); + .withUser("username1").withPassword("secret1") + .withParameters(new String[] {"param1:value1", "param2:value2"}).build(); config2 = new ConnectionConfigBuilder().withName("connection2").withUrl("url2") .withUser("username2").withPassword("secret2").build(); - service.addOrUpdateConnectionConfig(config1); - service.addOrUpdateConnectionConfig(config2); + service.createConnectionConfig(config1); + service.createConnectionConfig(config2); RegionMappingBuilder regionMappingBuilder1 = new RegionMappingBuilder() .withRegionName("regionName1").withPdxClassName("pdxClassName1").withTableName("tableName1") diff --git a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfigurationTest.java b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfigurationTest.java index 939db7c..13aff42 100644 --- a/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfigurationTest.java +++ b/geode-connectors/src/test/java/org/apache/geode/connectors/jdbc/internal/xml/JdbcServiceConfigurationTest.java @@ -118,8 +118,8 @@ public class JdbcServiceConfigurationTest { configuration.onCreate(cache, cache); - verify(service, times(1)).addOrUpdateConnectionConfig(connection1); - verify(service, times(1)).addOrUpdateConnectionConfig(connection2); + verify(service, times(1)).createConnectionConfig(connection1); + verify(service, times(1)).createConnectionConfig(connection2); } @Test diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java index fbb30cc..0476072 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/domain/XmlEntity.java @@ -148,6 +148,42 @@ public class XmlEntity implements VersionedDataSerializable { initializeSearchString(parentKey, parentValue, childPrefix, childKey, childValue); } + public XmlEntity(final String parentType, final String childPrefix, final String childNamespace, + final String childType) { + this.parentType = parentType; + this.type = childType; + this.childPrefix = childPrefix; + this.childNamespace = childNamespace; + + StringBuilder sb = new StringBuilder(); + sb.append("//").append(this.parentType); + sb.append("/").append(childPrefix).append(':').append(this.type); + this.searchString = sb.toString(); + this.xmlDefinition = parseXmlForDefinition(); + } + + private String parseXmlForDefinition() { + final Cache cache = CacheFactory.getAnyInstance(); + + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(stringWriter); + CacheXmlGenerator.generate(cache, printWriter, true, false, false); + printWriter.close(); + InputSource inputSource = new InputSource(new StringReader(stringWriter.toString())); + try { + Document document = XmlUtils.getDocumentBuilder().parse(inputSource); + Node element = document.getElementsByTagNameNS(childNamespace, type).item(0); + if (null != element) { + return XmlUtils.elementToString(element); + } + } catch (Exception e) { + throw new InternalGemFireError("Could not parse XML when creating XMLEntity", e); + } + logger.warn("No XML definition could be found with name={} and attributes={}", type, + attributes); + return null; + } + private void initializeSearchString(final String parentKey, final String parentValue, final String childPrefix, final String childKey, final String childValue) { StringBuffer sb = new StringBuffer(); @@ -206,7 +242,6 @@ public class XmlEntity implements VersionedDataSerializable { * @since GemFire 8.1 */ private String loadXmlDefinition(final String xmlDocument) { - final Cache cache = CacheFactory.getAnyInstance(); try { InputSource inputSource = new InputSource(new StringReader(xmlDocument)); return loadXmlDefinition(XmlUtils.getDocumentBuilder().parse(inputSource)); @@ -226,16 +261,16 @@ public class XmlEntity implements VersionedDataSerializable { * @throws TransformerFactoryConfigurationError * @since GemFire 8.1 */ - private String loadXmlDefinition(final Document document) + public String loadXmlDefinition(final Document document) throws XPathExpressionException, TransformerFactoryConfigurationError, TransformerException { - final Cache cache = CacheFactory.getAnyInstance(); - this.searchString = createQueryString(prefix, type, attributes); logger.info("XmlEntity:searchString: {}", this.searchString); if (document != null) { XPathContext xpathContext = new XPathContext(); xpathContext.addNamespace(prefix, namespace); + xpathContext.addNamespace(childPrefix, childNamespace); // TODO: wrap this line with + // conditional // Create an XPathContext here Node element = XmlUtils.querySingleElement(document, this.searchString, xpathContext); // Must copy to preserve namespaces. -- To stop receiving notification emails like this one, please contact "commits@geode.apache.org" .