diff --git a/.gitignore b/.gitignore index 63b666d..207ce65 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ target .project .settings .classpath +.factorypath # Mobile Tools for Java (J2ME) .mtj.tmp/ diff --git a/connectors/pom.xml b/connectors/pom.xml index 12d3abf..731bb6f 100644 --- a/connectors/pom.xml +++ b/connectors/pom.xml @@ -36,6 +36,7 @@ twitter-mention-connector twitter-search-connector salesforce-upsert-contact-connector + sql-stored-connector day-trade-get-connector day-trade-place-connector trade-insight-buy-connector diff --git a/connectors/sql-stored-connector/.gitignore b/connectors/sql-stored-connector/.gitignore new file mode 100644 index 0000000..1298c5f --- /dev/null +++ b/connectors/sql-stored-connector/.gitignore @@ -0,0 +1 @@ +derby.log diff --git a/connectors/sql-stored-connector/pom.xml b/connectors/sql-stored-connector/pom.xml new file mode 100644 index 0000000..479fd2e --- /dev/null +++ b/connectors/sql-stored-connector/pom.xml @@ -0,0 +1,221 @@ + + + + 4.0.0 + + + io.syndesis + connectors + 0.4-SNAPSHOT + + + sql-stored-connector + jar + Syndesis Connectors :: Sql Stored Connector + + + UTF-8 + UTF-8 + 2.20.0.fuse-000091 + + + + + + + org.apache.camel + camel-parent + ${camel.version} + import + pom + + + + + + + + + org.apache.camel + camel-sql + + + org.apache.camel + camel-sql-starter + + + + + org.apache.camel + camel-connector + + + + + org.apache.camel + apt + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + + commons-dbcp + commons-dbcp + + + + + org.apache.derby + derby + + + org.postgresql + postgresql + + + + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + + + org.apache.camel + camel-test + test + + + com.fasterxml.jackson.core + jackson-databind + test + + + com.fasterxml.jackson.module + jackson-module-jsonSchema + test + + + + + install + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.1 + + UTF-8 + + + + + + + + + + + + + jboss-ea + JBoss Early-access repository + https://repository.jboss.org/nexus/content/groups/ea/ + + true + + + false + + + + + + diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/ColumnMode.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/ColumnMode.java new file mode 100644 index 0000000..9b799bc --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/ColumnMode.java @@ -0,0 +1,33 @@ +/** + * 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 io.syndesis.connector; + +/** + * Complete Enumeration of Column modes in use by + * Stored Procedures. + * + * @since 09/11/17 + * @author kstam + * + */ +public enum ColumnMode { + UNKNOWN, IN, INOUT, RESULT, OUT, RETURN; + + public static ColumnMode valueOf(int columnType) { + return ColumnMode.values()[columnType]; + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/DatabaseProduct.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/DatabaseProduct.java new file mode 100644 index 0000000..e5fb6e8 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/DatabaseProduct.java @@ -0,0 +1,41 @@ +/** + * 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 io.syndesis.connector; + +/** + * Enumeration of Database products we have tested, and + * for which we ship drivers for. One caviat is the + * Oracle Driver which cannot be shipped due to + * restrictions on its license. + * + * @since 09/11/17 + * @author kstam + * + */ +public enum DatabaseProduct { + APACHE_DERBY, ORACLE, POSTGRESQL; + + /** + * Can be used to convert '_' to ' ' in the enum + * name. + * + * @return name of the enum. + */ + public String nameWithSpaces() { + return name().replaceAll("_", " "); + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanSchemaBuilder.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanSchemaBuilder.java new file mode 100644 index 0000000..6db5283 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanSchemaBuilder.java @@ -0,0 +1,65 @@ +/** + * 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 io.syndesis.connector; + +import java.util.ArrayList; +import java.util.List; + +/** + * JSON Schema builder for the simple schema's the SQL Stored Connector uses. + * + * The schema is created so it can be passed on to the dataMapper. An example + * schema looks like + * + *
+ * {@code
+ * "$schema": "http://json-schema.org/schema#",
+ * "type" : "object",
+ * "properties" : {
+ *   "a" : {"type" : "integer"},
+ *   "b" : {"type" : "integer"}
+ *  } 
+ * }
+ * 
+ * + * @author kstam + * @since 09/11/2017 + * + */ +public class JSONBeanSchemaBuilder { + + final static String SCHEMA = "{\n" + + " \"$schema\": \"http://json-schema.org/schema#\",\n" + + " \"type\" : \"object\",\n" + + " \"properties\" : {\n%s\n } \n}"; + List fields = new ArrayList(); + + public JSONBeanSchemaBuilder addField(String name, String type) { + fields.add(String.format(" \"%s\" : {\"type\" : \"%s\"}", name, type)); + return this; + } + + public String build() { + String fieldString = ""; + int count = 0; + for (String field : fields) { + fieldString += field; + if ( fields.size()!=++count ) fieldString += ",\n"; + } + return String.format(SCHEMA, fieldString); + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanUtil.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanUtil.java new file mode 100644 index 0000000..cb32747 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/JSONBeanUtil.java @@ -0,0 +1,54 @@ +/** + * 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 io.syndesis.connector; + +import java.util.Iterator; +import java.util.Properties; + +import org.json.JSONObject; + +/** + * Utility to help with parsing the data from the simple + * serialized java bean. The resulting property map can + * then be used by the SqlStoredComponentConnector. + * + * @author kstam + * + */ +public class JSONBeanUtil { + + /** + * Convenience method to parse the properties from a simple BeanJSON. + * Properties can be read by Camel. + * + * @param json simple JSON represenation of a Java Bean used + * as input Data for the SqlStoredConnector + * @return Properties representation of the simple JSON bean + */ + public static Properties parsePropertiesFromJSONBean(String json) { + Properties properties = new Properties(); + JSONObject obj = new JSONObject(json); + @SuppressWarnings("unchecked") + Iterator iterator = obj.keys(); + while (iterator.hasNext()) { + String key = iterator.next(); + String value = String.valueOf(obj.get(key)); + properties.setProperty(key, value); + } + return properties; + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorComponent.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorComponent.java new file mode 100644 index 0000000..2272f13 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorComponent.java @@ -0,0 +1,55 @@ +/** + * 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 io.syndesis.connector; + +import java.util.Properties; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.component.connector.DefaultConnectorComponent; + +/** + * Camel SqlStoredConnector connector + */ +public class SqlStoredConnectorComponent extends DefaultConnectorComponent { + + + + public SqlStoredConnectorComponent() { + super("sqlStoredConnector", "io.syndesis.connector.SqlStoredConnectorComponent"); + registerExtension(SqlStoredConnectorVerifierExtension::new); + registerExtension(SqlStoredConnectorMetaDataExtension::new); + } + + @Override + public Processor getBeforeProducer() { + + Processor processor = new Processor() { + public void process(Exchange exchange) + throws Exception { + System.out.println(exchange.getIn() + .getBody().getClass()); + String body = (String) exchange.getIn().getBody(); + Properties properties = JSONBeanUtil.parsePropertiesFromJSONBean(body); + exchange.getIn().setBody(properties); + } + }; + return processor; + } + + +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorMetaDataExtension.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorMetaDataExtension.java new file mode 100644 index 0000000..cf96fd1 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorMetaDataExtension.java @@ -0,0 +1,159 @@ +/** + * 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 io.syndesis.connector; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.JDBCType; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.camel.component.extension.metadata.AbstractMetaDataExtension; +import org.apache.camel.component.extension.metadata.DefaultMetaData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SqlStoredConnectorMetaDataExtension extends AbstractMetaDataExtension { + + private static final Logger LOGGER = LoggerFactory.getLogger(SqlStoredConnectorMetaDataExtension.class); + + @Override + public Optional meta(Map properties) { + + MetaData metaData = null; + + Map list = getStoredProcedures(properties); + metaData = new DefaultMetaData(null, null, list); + return Optional.of(metaData); + } + + protected Map getStoredProcedures(Map parameters) { + + Map storedProcedures = new HashMap(); + ResultSet procedureSet = null; + + try (Connection connection = DriverManager.getConnection( + String.valueOf(parameters.get("url")), + String.valueOf(parameters.get("user")), + String.valueOf(parameters.get("password")));) { + + DatabaseMetaData meta = connection.getMetaData(); + String catalog = (String) parameters.getOrDefault("catalog", null); + String defaultSchema = getDefaultSchema(meta.getDatabaseProductName(), parameters); + String schemaPattern = (String) parameters.getOrDefault("schema-pattern", defaultSchema); + String procedurePattern = (String) parameters.getOrDefault("procedure-pattern", null); + + if (meta.getDatabaseProductName().equalsIgnoreCase(DatabaseProduct.POSTGRESQL.name())) { + procedureSet = meta.getFunctions(catalog, schemaPattern, procedurePattern); + } else { + procedureSet = meta.getProcedures(catalog, schemaPattern, procedurePattern); + } + while (procedureSet.next()) { + String name = procedureSet.getString("PROCEDURE_NAME"); + StoredProcedureMetadata storedProcedureMetadata = + getStoredProcedureMetadata(connection, catalog, schemaPattern, name); + storedProcedureMetadata.setName(procedureSet.getString("PROCEDURE_NAME")); + storedProcedureMetadata.setType(procedureSet.getString("PROCEDURE_TYPE")); + storedProcedureMetadata.setRemark(procedureSet.getString("REMARKS")); + storedProcedures.put(storedProcedureMetadata.getName(), storedProcedureMetadata); + } + return storedProcedures; + + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + if (procedureSet != null) { + try { + if (!procedureSet.isClosed()) { + procedureSet.close(); + } + } catch (SQLException e) { + LOGGER.warn(e.getMessage()); + } + } + } + } + + protected String getDefaultSchema(String databaseProductName, Map parameters) { + + String defaultSchema = null; + // Oracle uses the username as schema + if (databaseProductName.equalsIgnoreCase(DatabaseProduct.ORACLE.name())) { + defaultSchema = parameters.get("user").toString(); + } else if (databaseProductName.equalsIgnoreCase(DatabaseProduct.POSTGRESQL.name())) { + defaultSchema = "public"; + } else if (databaseProductName.equalsIgnoreCase(DatabaseProduct.APACHE_DERBY.nameWithSpaces())) { + if (parameters.get("user") != null) { + defaultSchema = parameters.get("user").toString().toUpperCase(); + } else { + defaultSchema = "NULL"; + } + } + return defaultSchema; + } + + protected StoredProcedureMetadata getStoredProcedureMetadata(Connection connection, String catalog, String schema, + String procedureName) { + + ResultSet columnSet = null; + StoredProcedureMetadata storedProcedureMetadata = new StoredProcedureMetadata(); + storedProcedureMetadata.setName(procedureName); + try { + DatabaseMetaData meta = connection.getMetaData(); + if (meta.getDatabaseProductName().equalsIgnoreCase(DatabaseProduct.POSTGRESQL.name())) { + columnSet = meta.getFunctionColumns(catalog, schema, procedureName, null); + } else { + columnSet = meta.getProcedureColumns(catalog, schema, procedureName, null); + } + + List columnList = new ArrayList(); + String template = procedureName + "("; + while (columnSet.next()) { + StoredProcedureColumn column = new StoredProcedureColumn(); + column.setName(columnSet.getString("COLUMN_NAME")); + column.setMode(ColumnMode.valueOf(columnSet.getInt("COLUMN_TYPE"))); + column.setJdbcType(JDBCType.valueOf(columnSet.getInt("DATA_TYPE"))); + template += " " + column.getMode().name() + " " + column.getJdbcType() + " ${body[" + column.getName() + "], "; + columnList.add(column); + } + template = template.substring(0, template.length() - 2) + ")"; + storedProcedureMetadata.setTemplate(template); + storedProcedureMetadata.setColumnList(columnList); + return storedProcedureMetadata; + + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + if (columnSet != null) { + try { + if (!columnSet.isClosed()) { + columnSet.close(); + } + } catch (SQLException e) { + LOGGER.warn(e.getMessage()); + } + } + } + } + +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorVerifierExtension.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorVerifierExtension.java new file mode 100644 index 0000000..cd53953 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/SqlStoredConnectorVerifierExtension.java @@ -0,0 +1,96 @@ +/** + * 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 io.syndesis.connector; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Map; + +import org.apache.camel.component.extension.verifier.DefaultComponentVerifierExtension; +import org.apache.camel.component.extension.verifier.ResultBuilder; +import org.apache.camel.component.extension.verifier.ResultErrorBuilder; +import org.apache.camel.component.extension.verifier.ResultErrorHelper; + +public class SqlStoredConnectorVerifierExtension extends DefaultComponentVerifierExtension { + + public SqlStoredConnectorVerifierExtension() { + super("sql-stored-connector"); + } + + public SqlStoredConnectorVerifierExtension(String scheme) { + super(scheme); + } + + // ********************************* + // Parameters validation + // ********************************* + + @Override + protected Result verifyParameters(Map parameters) { + ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS) + .error(ResultErrorHelper.requiresOption("url", parameters)); + + + if (builder.build().getErrors().isEmpty()) { + try (Connection connection = + DriverManager.getConnection( + parameters.get("url").toString(), + String.valueOf(parameters.get("user")), + String.valueOf(parameters.get("password")))) { + } catch (SQLException e) { + String supportedDatabases = String.join(",", + Arrays.stream(DatabaseProduct.values()) + .map(Enum::name) + .toArray(String[]::new)); + String msg = "Supported Databases are [" + supportedDatabases + "]"; + builder.error(ResultErrorBuilder.withCodeAndDescription( + VerificationError.StandardCode.UNSUPPORTED, + msg).build()).build(); + } + } + return builder.build(); + + } + + // ********************************* + // Connectivity validation + // ********************************* + + @Override + protected Result verifyConnectivity(Map parameters) { + return ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY) + .error(parameters, this::verifyCredentials) + .build(); + } + + private void verifyCredentials(ResultBuilder builder, Map parameters) { + try ( Connection connection = DriverManager.getConnection( + parameters.get("url").toString(), + String.valueOf(parameters.get("user")), + String.valueOf(parameters.get("password")))) { + if (connection == null) { + throw new SQLException("No Connection"); + } + } catch (Exception e) { + ResultErrorBuilder errorBuilder = ResultErrorBuilder.withCodeAndDescription( + VerificationError.StandardCode.AUTHENTICATION, e.getMessage()); + builder.error(errorBuilder.build()); + } + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureColumn.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureColumn.java new file mode 100644 index 0000000..9c98df2 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureColumn.java @@ -0,0 +1,52 @@ +/** + * 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 io.syndesis.connector; + +import java.sql.JDBCType; + +public class StoredProcedureColumn { + + private int ordinal; + private String name; + private ColumnMode mode; + private JDBCType jdbcType; + + public int getOrdinal() { + return ordinal; + } + public void setOrdinal(int ordinal) { + this.ordinal = ordinal; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public ColumnMode getMode() { + return mode; + } + public void setMode(ColumnMode mode) { + this.mode = mode; + } + public JDBCType getJdbcType() { + return jdbcType; + } + public void setJdbcType(JDBCType jdbcType) { + this.jdbcType = jdbcType; + } +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureMetadata.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureMetadata.java new file mode 100644 index 0000000..d146251 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/StoredProcedureMetadata.java @@ -0,0 +1,59 @@ +/** + * 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 io.syndesis.connector; + +import java.util.List; + +public class StoredProcedureMetadata { + private String name; + private String type; + private String remark; + private List columnList; + private String template; + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getRemark() { + return remark; + } + public void setRemark(String remark) { + this.remark = remark; + } + public List getColumnList() { + return columnList; + } + public void setColumnList(List columnList) { + this.columnList = columnList; + } + public String getTemplate() { + return template; + } + public void setTemplate(String template) { + this.template = template; + } + +} diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorAutoConfiguration.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorAutoConfiguration.java new file mode 100644 index 0000000..8c92c47 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorAutoConfiguration.java @@ -0,0 +1,115 @@ +/** + * 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 io.syndesis.connector.springboot; + +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Generated; +import javax.annotation.PostConstruct; + +import io.syndesis.connector.SqlStoredConnectorComponent; +import org.apache.camel.CamelContext; +import org.apache.camel.util.IntrospectionSupport; +import org.apache.commons.dbcp.BasicDataSource; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * Generated by camel-connector-maven-plugin - do not edit this file! + */ +@Generated("org.apache.camel.maven.connector.SpringBootAutoConfigurationMojo") +@Configuration +@ConditionalOnBean(type = "org.apache.camel.spring.boot.CamelAutoConfiguration") +@AutoConfigureAfter(name = "org.apache.camel.spring.boot.CamelAutoConfiguration") +@EnableConfigurationProperties(SqlStoredConnectorConnectorConfiguration.class) +public class SqlStoredConnectorConnectorAutoConfiguration { + + @Autowired + private CamelContext camelContext; + @Autowired + private SqlStoredConnectorConnectorConfiguration configuration; + + @Lazy + @Bean(name = "sql-stored-connector-component") + @ConditionalOnClass(CamelContext.class) + @ConditionalOnMissingBean(name = "sql-stored-connector-component") + public SqlStoredConnectorComponent configureSqlStoredConnectorComponent() + throws Exception { + SqlStoredConnectorComponent connector = new SqlStoredConnectorComponent(); + connector.setCamelContext(camelContext); + Map parameters = new HashMap<>(); + IntrospectionSupport.getProperties(configuration, parameters, null, + false); + IntrospectionSupport.setProperties(camelContext, + camelContext.getTypeConverter(), connector, parameters); + connector.setOptions(parameters); + return connector; + } + + @PostConstruct + public void postConstructSqlStoredConnectorComponent() { + if (camelContext != null) { + Map parameters = new HashMap<>(); + for (Map.Entry entry : configuration + .getConfigurations().entrySet()) { + parameters.clear(); + SqlStoredConnectorComponent connector = new SqlStoredConnectorComponent(); + connector.setCamelContext(camelContext); + try { + IntrospectionSupport.getProperties(entry.getValue(), + parameters, null, false); + IntrospectionSupport.setProperties(camelContext, + camelContext.getTypeConverter(), connector, + parameters); + connector.setOptions(parameters); + camelContext.addComponent(entry.getKey(), connector); + } catch (Exception e) { + throw new BeanCreationException(entry.getKey(), + e.getMessage(), e); + } + } + try { + + createDataSource(configuration); + } catch (Exception e) { + throw new BeanCreationException( + e.getMessage(), e); + } + } + } + + private void createDataSource(SqlStoredConnectorConnectorConfiguration configuration) throws ClassNotFoundException { + + BasicDataSource ds = new BasicDataSource(); + ds.setUsername(configuration.getUser()); + configuration.setUser(null); + ds.setPassword(configuration.getPassword()); + configuration.setPassword(null); + ds.setUrl(configuration.getUrl()); + configuration.setUrl(null); + + configuration.setDataSource(ds); + } +} \ No newline at end of file diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfiguration.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfiguration.java new file mode 100644 index 0000000..812baaa --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfiguration.java @@ -0,0 +1,39 @@ +/** + * 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 io.syndesis.connector.springboot; + +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Generated; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Generated("org.apache.camel.maven.connector.SpringBootAutoConfigurationMojo") +@ConfigurationProperties(prefix = "sql-stored-connector") +public class SqlStoredConnectorConnectorConfiguration + extends + SqlStoredConnectorConnectorConfigurationCommon { + + /** + * Define additional configuration definitions + */ + private Map configurations = new HashMap<>(); + + public Map getConfigurations() { + + return configurations; + } +} \ No newline at end of file diff --git a/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfigurationCommon.java b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfigurationCommon.java new file mode 100644 index 0000000..a98b444 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/java/io/syndesis/connector/springboot/SqlStoredConnectorConnectorConfigurationCommon.java @@ -0,0 +1,128 @@ +/** + * 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 io.syndesis.connector.springboot; + +import javax.annotation.Generated; +import javax.sql.DataSource; + +/** + * The sql connector allows you to work with databases using JDBC Stored + * Procedure queries. + * + * Generated by camel-package-maven-plugin - do not edit this file! + */ +@Generated("org.apache.camel.maven.connector.SpringBootAutoConfigurationMojo") +public class SqlStoredConnectorConnectorConfigurationCommon { + + /** + * Sets the DataSource to use to communicate with the database. + */ + private DataSource dataSource; + /** + * Sets the StoredProcedure template to perform + */ + private String template; + /** + * Enables or disables batch mode + */ + private boolean batch = false; + /** + * If set will ignore the results of the template and use the existing IN + * message as the OUT message for the continuation of processing + */ + private boolean noop = false; + /** + * Sets the user to use for the database connection + */ + private String user; + /** + * Sets the password to use for the database connection + */ + private String password; + /** + * Sets the driver class to use for the database connection + */ + private String driver; + /** + * Sets the connection url to use for the database connection + */ + private String url; + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + + public boolean isBatch() { + return batch; + } + + public void setBatch(boolean batch) { + this.batch = batch; + } + + public boolean isNoop() { + return noop; + } + + public void setNoop(boolean noop) { + this.noop = noop; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} \ No newline at end of file diff --git a/connectors/sql-stored-connector/src/main/resources/META-INF/services/org/apache/camel/component/sql-stored-connector b/connectors/sql-stored-connector/src/main/resources/META-INF/services/org/apache/camel/component/sql-stored-connector new file mode 100644 index 0000000..d26e3e1 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/resources/META-INF/services/org/apache/camel/component/sql-stored-connector @@ -0,0 +1 @@ +class=io.syndesis.connector.SqlStoredConnectorComponent \ No newline at end of file diff --git a/connectors/sql-stored-connector/src/main/resources/META-INF/spring.factories b/connectors/sql-stored-connector/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..dbe4c6a --- /dev/null +++ b/connectors/sql-stored-connector/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ + +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.syndesis.connector.springboot.SqlStoredConnectorConnectorAutoConfiguration diff --git a/connectors/sql-stored-connector/src/main/resources/camel-connector-schema.json b/connectors/sql-stored-connector/src/main/resources/camel-connector-schema.json new file mode 100644 index 0000000..4e91714 --- /dev/null +++ b/connectors/sql-stored-connector/src/main/resources/camel-connector-schema.json @@ -0,0 +1,32 @@ +{ + "component": { + "kind": "component", + "baseScheme": "sql-stored", + "scheme": "sql-stored-connector", + "syntax": "sql-stored-connector:template", + "title": "SqlStoredConnector", + "description": "SQL Stored Procedure Connector to invoke a SQL Stored Procedure", + "label": "sql-stored", + "deprecated": false, + "async": false, + "producerOnly": true, + "lenientProperties": false, + "javaType": "io.syndesis.connector.SqlStoredConnectorComponent", + "groupId": "io.syndesis", + "artifactId": "sql-stored-connector", + "version": "0.4-SNAPSHOT" + }, + "componentProperties": { + "url": { "kind": "path", "displayName": "Connection URL", "group": "producer", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Sets the database URL" }, + "user": { "kind": "path", "displayName": "User", "group": "producer", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Sets database user" }, + "password": { "kind": "path", "displayName": "Password", "group": "producer", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": true, "description": "Sets the database password" }, + "schema": { "kind": "path", "displayName": "Schema", "group": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Sets database schema" }, + "catalog": { "kind": "path", "displayName": "Catalog", "group": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Sets database catalog" } + + }, + "properties": { + "template": { "kind": "path", "displayName": "Template", "group": "producer", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Sets the StoredProcedure template to perform" }, + "batch": { "kind": "parameter", "displayName": "Batch", "group": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Enables or disables batch mode" }, + "noop": { "kind": "parameter", "displayName": "Noop", "group": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "If set will ignore the results of the template and use the existing IN message as the OUT message for the continuation of processing" } + } +} diff --git a/connectors/sql-stored-connector/src/main/resources/camel-connector.json b/connectors/sql-stored-connector/src/main/resources/camel-connector.json new file mode 100644 index 0000000..665639f --- /dev/null +++ b/connectors/sql-stored-connector/src/main/resources/camel-connector.json @@ -0,0 +1,21 @@ +{ + "baseScheme" : "sql-stored", + "baseGroupId" : "org.apache.camel", + "baseArtifactId" : "camel-sql", + "baseVersion" : "2.20.0-SNAPSHOT", + "baseJavaType" : "org.apache.camel.component.sql.stored.SqlStoredComponent", + "name" : "SqlStoredConnector", + "scheme" : "sql-stored-connector", + "javaType" : "io.syndesis.connector.SqlStoredConnectorComponent", + "groupId" : "io.syndesis", + "artifactId" : "sql-stored-connector", + "version" : "0.4-SNAPSHOT", + "description" : "SQL Stored Procedure Connector to invoke a SQL Stored Procedure", + "labels" : [ "sql-stored" ], + "pattern" : "To", + "inputDataType" : "*", + "outputDataType" : "none", + "componentOptions" : [ "user", "password", "url", "schema", "catalog" ], + "endpointOptions" : [ "template", "batch", "noop" ], + "endpointValues" : {} +} \ No newline at end of file diff --git a/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/JSONSchemaBuilderTest.java b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/JSONSchemaBuilderTest.java new file mode 100644 index 0000000..996204a --- /dev/null +++ b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/JSONSchemaBuilderTest.java @@ -0,0 +1,93 @@ +/** + * 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 io.syndesis.connector; + +import java.util.Properties; + +import org.junit.Assert; +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; +import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; + +public class JSONSchemaBuilderTest { + + @Test + public void schemaTest() throws JsonProcessingException { + + /* Create Schema using Jackson */ + ObjectMapper mapper = new ObjectMapper(); + JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper); + JsonSchema schema = schemaGen.generateSchema(SimpleInputBean.class); + String desiredSchema = (mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema)); + System.out.println(desiredSchema); + //Manually cleaned up version up of desiredSchema to more easily test equality + desiredSchema = "{\n" + + " \"$schema\": \"http://json-schema.org/schema#\",\n" + + " \"type\" : \"object\",\n" + + " \"properties\" : {\n" + + " \"a\" : {\"type\" : \"integer\"},\n" + + " \"b\" : {\"type\" : \"integer\"}\n" + + " } \n" + + "}"; + String schemaFromBuilder = new JSONBeanSchemaBuilder() + .addField("a", "integer") + .addField("b", "integer").build(); + System.out.println(schemaFromBuilder); + Assert.assertEquals(desiredSchema, schemaFromBuilder); + } + + @Test + public void parsePropertiesFromJSONBeanTest() throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + SimpleInputBean bean = new SimpleInputBean(); + bean.setA(20); + bean.setB(30); + String jsonBean = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean); + + Properties properties = JSONBeanUtil.parsePropertiesFromJSONBean(jsonBean); + Assert.assertTrue(properties.containsKey("a")); + Assert.assertEquals("20", properties.get("a")); + Assert.assertTrue(properties.containsKey("b")); + Assert.assertEquals("30", properties.get("b")); + } + + class SimpleInputBean { + + int a; + int b; + + public int getA() { + return a; + } + + public void setA(int a) { + this.a = a; + } + + public int getB() { + return b; + } + + public void setB(int b) { + this.b = b; + } + } + +} diff --git a/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SampleStoredProcedures.java b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SampleStoredProcedures.java new file mode 100644 index 0000000..32f75e4 --- /dev/null +++ b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SampleStoredProcedures.java @@ -0,0 +1,66 @@ +/** + * 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 io.syndesis.connector; + +import java.sql.SQLException; + +public class SampleStoredProcedures { + + public static String DERBY_DEMO_ADD_SQL = + "CREATE PROCEDURE DEMO_ADD( IN A INTEGER, IN B INTEGER, OUT C INTEGER ) " + + "PARAMETER STYLE JAVA " + + "LANGUAGE JAVA " + + "EXTERNAL NAME 'io.syndesis.connector.SampleStoredProcedures.demo_add'"; + + public static String ORACLE_DEMO_ADD_SQL = + "create or replace PROCEDURE DEMO_ADD \n" + + "(\n" + + " A IN INTEGER\n" + + ", B IN INTEGER \n" + + ", C OUT INTEGER \n" + + ") AS \n" + + "BEGIN\n" + + " c := a + b;\n" + + "END DEMO_ADD;"; + + public static String POSTGRES_DEMO_ADD_SQL = + "CREATE OR REPLACE FUNCTION public.demo_add(\n" + + " a numeric,\n" + + " b numeric,\n" + + " OUT c numeric)\n" + + " RETURNS numeric\n" + + " LANGUAGE 'plpgsql'\n" + + "\n" + + "AS $BODY$\n" + + "\n" + + "BEGIN\n" + + " c := a + b;\n" + + " return;\n" + + "END; \n" + + "$BODY$;\n" + + "\n" + + "ALTER FUNCTION public.demo_add(numeric, numeric)\n" + + " OWNER TO postgres;"; + + public static void demo_add( + int a /* IN parameter */, + int b /* IN parameter */, + int[] c /* OUT parameter */) throws SQLException { + + c[0] = a + b; + } +} diff --git a/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredCommon.java b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredCommon.java new file mode 100644 index 0000000..9913b27 --- /dev/null +++ b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredCommon.java @@ -0,0 +1,76 @@ +/** + * 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 io.syndesis.connector; + +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class SqlStoredCommon { + + + + public Connection setupConnectionAndStoredProcedure(Connection connection, Properties properties) throws Exception { + + InputStream is = SqlStoredCommon.class.getClassLoader().getResourceAsStream("application.properties"); + properties.load(is); + String user = String.valueOf(properties.get("sql-stored-connector.user")); + String password = String.valueOf(properties.get("sql-stored-connector.password")); + String url = String.valueOf(properties.get("sql-stored-connector.url")); + + System.out.println("Connecting to the database for unit tests"); + try { + connection = DriverManager.getConnection(url,user,password); + String databaseProductName = connection.getMetaData().getDatabaseProductName(); + SqlStoredConnectorMetaDataExtension ext = new SqlStoredConnectorMetaDataExtension(); + Map parameters = new HashMap(); + for (final String name: properties.stringPropertyNames()) { + parameters.put(name.substring(name.indexOf(".")+1), properties.getProperty(name)); + } + Map storedProcedures = ext.getStoredProcedures(parameters); + + if (!storedProcedures.keySet().contains("DEMO_ADD") + && databaseProductName.equalsIgnoreCase(DatabaseProduct.APACHE_DERBY.nameWithSpaces())) { + try (Statement stmt = connection.createStatement()) { + stmt.execute(SampleStoredProcedures.DERBY_DEMO_ADD_SQL); + System.out.println("Created procedure " + SampleStoredProcedures.DERBY_DEMO_ADD_SQL); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception during Stored Procedure Creation."); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + fail("Exception during database startup."); + } + return connection; + } + + public void closeConnection(Connection connection) throws SQLException { + if (connection!=null && !connection.isClosed()) { + connection.close(); + } + } + +} diff --git a/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredConnectorComponentTest.java b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredConnectorComponentTest.java new file mode 100644 index 0000000..a2eadbb --- /dev/null +++ b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredConnectorComponentTest.java @@ -0,0 +1,112 @@ +/** + * 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 io.syndesis.connector; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.impl.SimpleRegistry; +import org.apache.commons.dbcp.BasicDataSource; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SqlStoredConnectorComponentTest { + + private static Connection connection; + private static Properties properties = new Properties(); + private static SqlStoredCommon sqlStoredCommon; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + sqlStoredCommon = new SqlStoredCommon(); + connection = sqlStoredCommon.setupConnectionAndStoredProcedure(connection, properties); + } + + @AfterClass + public static void afterClass() throws SQLException { + sqlStoredCommon.closeConnection(connection); + } + + @Test + public void camelConnectorTest() throws Exception { + + BasicDataSource ds = new BasicDataSource(); + ds.setUsername(properties.getProperty("sql-stored-connector.user")); + ds.setPassword(properties.getProperty("sql-stored-connector.password")); + ds.setUrl( properties.getProperty("sql-stored-connector.url")); + + SimpleRegistry registry = new SimpleRegistry(); + registry.put("dataSource", ds); + CamelContext context = new DefaultCamelContext(registry); + + String jsonBody = "{\"a\":20,\"b\":30}"; + CountDownLatch latch = new CountDownLatch(1); + + final Result result = new Result(); + + try { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("timer://myTimer?period=2000") + .setBody().constant(jsonBody) + .to("sql-stored-connector:DEMO_ADD( INTEGER ${body[a]}, INTEGER ${body[b]}, OUT INTEGER c)") + .process(new Processor() { + public void process(Exchange exchange) + throws Exception { + System.out.println(exchange.getIn() + .getBody().getClass()); + @SuppressWarnings("unchecked") + Map map = (Map) exchange.getIn().getBody(); + System.out.println(exchange.getIn() + .getBody()); + result.setResult(map); + latch.countDown(); + } + }); + } + }); + context.start(); + latch.await(5l,TimeUnit.SECONDS); + Assert.assertEquals("50", String.valueOf(result.getMap().get("c"))); + } finally { + context.stop(); + } + } + + class Result { + Map map; + + public Map getMap() { + return map; + } + public void setResult(Map map) { + this.map = map; + } + } +} diff --git a/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredProcedureTest.java b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredProcedureTest.java new file mode 100644 index 0000000..2e25a77 --- /dev/null +++ b/connectors/sql-stored-connector/src/test/java/io/syndesis/connector/SqlStoredProcedureTest.java @@ -0,0 +1,116 @@ +/** + * 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 io.syndesis.connector; + +import static org.junit.Assert.assertTrue; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SqlStoredProcedureTest { + + private static Connection connection; + private static Properties properties = new Properties(); + private static SqlStoredCommon sqlStoredCommon; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + sqlStoredCommon = new SqlStoredCommon(); + connection = sqlStoredCommon.setupConnectionAndStoredProcedure(connection, properties); + } + + @AfterClass + public static void afterClass() throws SQLException { + sqlStoredCommon.closeConnection(connection); + } + + @Test + public void listAllStoredProcedures() { + + SqlStoredConnectorMetaDataExtension ext = new SqlStoredConnectorMetaDataExtension(); + Map parameters = new HashMap(); + for (final String name: properties.stringPropertyNames()) { + parameters.put(name.substring(name.indexOf(".")+1), properties.getProperty(name)); + } + Map storedProcedures = ext.getStoredProcedures(parameters); + assertTrue(storedProcedures.size() > 0); + //Find 'demo_add' + assertTrue(storedProcedures.keySet().contains("DEMO_ADD")); + + for (String storedProcedureName : storedProcedures.keySet()) { + StoredProcedureMetadata md = storedProcedures.get(storedProcedureName); + System.out.println(storedProcedureName + " : " + md.getTemplate()); + } + + //Inspect demo_add + StoredProcedureMetadata metaData = storedProcedures.get("DEMO_ADD"); + Assert.assertEquals("DEMO_ADD( IN INTEGER ${body[A], IN INTEGER ${body[B], OUT INTEGER ${body[C])",metaData.getTemplate()); + } + + @Test + public void listSchemasTest() throws SQLException { + + DatabaseMetaData meta = connection.getMetaData(); + String catalog = null; + String schemaPattern = null; + + System.out.println("Querying for all Schemas..."); + ResultSet schema = meta.getSchemas(catalog, schemaPattern); + int schemaCount=0; + while (schema.next()) { + String catalogName = schema.getString("TABLE_CATALOG"); + String schemaName = schema.getString("TABLE_SCHEM"); + System.out.println(catalogName + ":" + schemaName); + schemaCount++; + } + assertTrue(schemaCount > 0); + } + + @Test + public void callStoredProcedureTest() throws SQLException { + + String c = ""; + connection.setAutoCommit(true); + try (CallableStatement cStmt = connection.prepareCall("{call DEMO_ADD(?, ?, ?)}")) { + cStmt.setInt(1, 1); + cStmt.setInt(2, 2); + cStmt.registerOutParameter(3, Types.NUMERIC); + cStmt.execute(); + + c = cStmt.getBigDecimal(3).toPlainString(); + System.out.println("OUTPUT " + c); + Assert.assertEquals("3", c); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + +} diff --git a/connectors/sql-stored-connector/src/test/resources/application.properties b/connectors/sql-stored-connector/src/test/resources/application.properties new file mode 100644 index 0000000..1c40433 --- /dev/null +++ b/connectors/sql-stored-connector/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Oracle database configuration to use +# sql-stored-connector.url=jdbc:oracle:thin:@:: +# sql-stored-connector.user= +# sql-stored-connector.password= + +# Postgres database configuration +# sql-stored-connector.url=jdbc:postgresql://localhost:5432/test +# sql-stored-connector.user= +# sql-stored-connector.password= + +# Derby database configuration +sql-stored-connector.url=jdbc:derby:memory:testdb;create=true +sql-stored-connector.user=sa +sql-stored-connector.password="" \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 6d4c1f0..e8a6adf 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -37,6 +37,7 @@ http-pingcheck-example salesforce-pingcheck-example twitter-pingcheck-example + sql-stored-example diff --git a/examples/sql-stored-example/.gitignore b/examples/sql-stored-example/.gitignore new file mode 100644 index 0000000..1298c5f --- /dev/null +++ b/examples/sql-stored-example/.gitignore @@ -0,0 +1 @@ +derby.log diff --git a/examples/sql-stored-example/README.md b/examples/sql-stored-example/README.md new file mode 100644 index 0000000..810e7e8 --- /dev/null +++ b/examples/sql-stored-example/README.md @@ -0,0 +1,14 @@ +## Sql Stored Ping Check Example + +This example performs a ping check to a Database to check if the user credentials are valid +and that you can connect. + +### How to run + +This example can be run from the command line using: + + mvn exec:java + +### Configuring Credentials + +You can configure your own accounts in the `application.properties` file. diff --git a/examples/sql-stored-example/pom.xml b/examples/sql-stored-example/pom.xml new file mode 100644 index 0000000..c7a22d1 --- /dev/null +++ b/examples/sql-stored-example/pom.xml @@ -0,0 +1,94 @@ + + + + + + io.syndesis + examples + 0.4-SNAPSHOT + + 4.0.0 + + sql-stored-example + Syndesis Connectors :: Sql Stored Example + Validate database credentials can connect + jar + + + + + + io.syndesis + sql-stored-connector + ${project.version} + + + ch.qos.logback + logback-classic + + + + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.module + jackson-module-jsonSchema + 2.9.0 + + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-slf4j-impl + + + + org.hibernate + hibernate-validator + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.5.0 + + io.syndesis.example.SqlStoredApplication + false + + + + + + diff --git a/examples/sql-stored-example/src/main/java/io/syndesis/connector/SampleStoredProcedures.java b/examples/sql-stored-example/src/main/java/io/syndesis/connector/SampleStoredProcedures.java new file mode 100644 index 0000000..09505f0 --- /dev/null +++ b/examples/sql-stored-example/src/main/java/io/syndesis/connector/SampleStoredProcedures.java @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.syndesis.connector; + +public class SampleStoredProcedures { + + /** + * SQL to create the DEMO_ADD procedure in Apache Derby + */ + public static String DERBY_DEMO_ADD_SQL = + "CREATE PROCEDURE DEMO_ADD( IN A INTEGER, IN B INTEGER, OUT C INTEGER ) " + + "PARAMETER STYLE JAVA " + + "LANGUAGE JAVA " + + "EXTERNAL NAME 'io.syndesis.connector.SampleStoredProcedures.demo_add'"; + /** + * SQL to create the DEMO_ADD procedure in Oracle + */ + public static String ORACLE_DEMO_ADD_SQL = + "create or replace PROCEDURE DEMO_ADD \n" + + "(\n" + + " A IN INTEGER\n" + + ", B IN INTEGER \n" + + ", C OUT INTEGER \n" + + ") AS \n" + + "BEGIN\n" + + " c := a + b;\n" + + "END DEMO_ADD;"; + /** + * SQL to create the DEMO_APP procedure in Postgresql + */ + public static String POSTGRES_DEMO_ADD_SQL = + "CREATE OR REPLACE FUNCTION public.demo_add(\n" + + " a numeric,\n" + + " b numeric,\n" + + " OUT c numeric)\n" + + " RETURNS numeric\n" + + " LANGUAGE 'plpgsql'\n" + + "\n" + + "AS $BODY$\n" + + "\n" + + "BEGIN\n" + + " c := a + b;\n" + + " return;\n" + + "END; \n" + + "$BODY$;\n" + + "\n" + + "ALTER FUNCTION public.demo_add(numeric, numeric)\n" + + " OWNER TO postgres;"; + /** + * Java method implementing the Stored Procedure for Derby. + * + * @param a - input parameter of type integer + * @param b - input parameter of type integer + * @param c - output (the result of a + b) of type integer[] + */ + public static void demo_add( + int a /* IN parameter */, + int b /* IN parameter */, + int[] c /* OUT parameter */) { + + c[0] = a + b; + } +} diff --git a/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredApplication.java b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredApplication.java new file mode 100644 index 0000000..97a0885 --- /dev/null +++ b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredApplication.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.syndesis.example; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +import java.util.Properties; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import io.syndesis.connector.DatabaseProduct; +import io.syndesis.connector.SampleStoredProcedures; + +//CHECKSTYLE:OFF +@SpringBootApplication +public class SqlStoredApplication { + + /** + * A main method to start this application. + * @throws IOException + * @throws FileNotFoundException + * @throws SQLException + */ + public static void main(String[] args) throws FileNotFoundException, IOException, SQLException { + + Properties properties = new Properties(); + InputStream is = SqlStoredApplication.class.getClassLoader().getResourceAsStream("application.properties"); + properties.load(is); + String url = properties.getProperty("sql-stored-connector.url"); + String user = properties.getProperty("sql-stored-connector.user"); + String password = properties.getProperty("sql-stored-connector.password"); + + //create the stored procedure in the Derby + try (Connection connection = DriverManager.getConnection(url,user,password)) { + connection.setAutoCommit(true); + String databaseProductName = connection.getMetaData().getDatabaseProductName(); + if (databaseProductName.equalsIgnoreCase(DatabaseProduct.APACHE_DERBY.nameWithSpaces())) { + try (Statement stmt = connection.createStatement()) { + stmt.execute(SampleStoredProcedures.DERBY_DEMO_ADD_SQL); + System.out.println("Created procedure " + SampleStoredProcedures.DERBY_DEMO_ADD_SQL); + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + SpringApplication.run(SqlStoredApplication.class, args); + try { + Thread.sleep(60000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} +//CHECKSTYLE:ON \ No newline at end of file diff --git a/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheck.java b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheck.java new file mode 100644 index 0000000..e2b16fa --- /dev/null +++ b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheck.java @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.syndesis.example; + +import java.io.FileInputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import org.apache.camel.CamelContext; +import org.apache.camel.Component; +import org.apache.camel.component.extension.ComponentVerifierExtension; +import org.apache.camel.component.extension.ComponentVerifierExtension.Result; +import org.apache.camel.impl.DefaultCamelContext; + +import io.syndesis.connector.SqlStoredConnectorVerifierExtension; + +public class SqlStoredPingCheck { + + public void ping() throws Exception { + // need to create Camel + CamelContext camel = new DefaultCamelContext(); + camel.start(); + + // get the connector to use + Component sqlstored = camel.getComponent("sql-stored-connector"); + + // the connector must support ping check if its verifiable + Optional vce = sqlstored.getExtension(SqlStoredConnectorVerifierExtension.class); + if (vce.isPresent()) { + ComponentVerifierExtension verifier = vce.get(); + + Map parameters = loadParameters(); + + ComponentVerifierExtension.Result result = verifier.verify(ComponentVerifierExtension.Scope.PARAMETERS, parameters); + + System.out.println("============================================="); + System.out.println(""); + System.out.println("Parameters check result: " + result.getStatus()); + if (result.getStatus().equals(Result.Status.ERROR)) { + System.out.println(result.getErrors()); + } + System.out.println(""); + System.out.println("============================================="); + + ComponentVerifierExtension.Result result2 = verifier.verify(ComponentVerifierExtension.Scope.CONNECTIVITY, parameters); + + System.out.println("============================================="); + System.out.println(""); + System.out.println("Ping check result: " + result2.getStatus()); + if (result2.getStatus().equals(Result.Status.ERROR)) { + System.out.println(result2.getErrors()); + } + System.out.println(""); + System.out.println("============================================="); + + } else { + System.out.println("Component does not support ping check"); + } + + camel.stop(); + } + + /** + * Helper to load parameters from a .properties file + */ + private Map loadParameters() throws Exception { + Properties prop = new Properties(); + prop.load(new FileInputStream("src/main/resources/application.properties")); + + Map answer = new HashMap<>(); + Enumeration en = prop.propertyNames(); + while (en.hasMoreElements()) { + String key = (String) en.nextElement(); + if (key.startsWith("sql-stored-connector.")) { + String shortKey = key.substring(21, key.length()); + Object value = prop.getProperty(key); + answer.put(shortKey, value); + } + } + return answer; + } + + +} diff --git a/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheckMain.java b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheckMain.java new file mode 100644 index 0000000..aab059a --- /dev/null +++ b/examples/sql-stored-example/src/main/java/io/syndesis/example/SqlStoredPingCheckMain.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.syndesis.example; + +public class SqlStoredPingCheckMain { + + public static void main(String[] args) throws Exception { + + SqlStoredPingCheck check = new SqlStoredPingCheck(); + check.ping(); + System.exit(0); + } + +} diff --git a/examples/sql-stored-example/src/main/java/io/syndesis/example/TimerSqlStoredRoute.java b/examples/sql-stored-example/src/main/java/io/syndesis/example/TimerSqlStoredRoute.java new file mode 100644 index 0000000..e744190 --- /dev/null +++ b/examples/sql-stored-example/src/main/java/io/syndesis/example/TimerSqlStoredRoute.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2017 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.syndesis.example; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.stereotype.Component; + +@Component +public class TimerSqlStoredRoute extends RouteBuilder { + + @Override + public void configure() throws Exception { + + String jsonBody = "{\"a\":20,\"b\":30}"; + + from("timer://myTimer?period=2000") + .setBody().constant(jsonBody) + + //DEMO_ADD( IN INTEGER ${body[A], IN INTEGER ${body[B], OUT INTEGER ${body[C])",metaData.getTemplate()); + + .to("sql-stored-connector:DEMO_ADD( " + + "INTEGER ${body[a]}, " + + "INTEGER ${body[b]}, " + + "OUT INTEGER c)") + .process(new Processor() { + + public void process(Exchange exchange) + throws Exception { + System.out.println(exchange.getIn() + .getBody().getClass()); + System.out.println(exchange.getIn() + .getBody()); + } + }); + } +} diff --git a/examples/sql-stored-example/src/main/resources/application.properties b/examples/sql-stored-example/src/main/resources/application.properties new file mode 100644 index 0000000..01a0a33 --- /dev/null +++ b/examples/sql-stored-example/src/main/resources/application.properties @@ -0,0 +1,17 @@ +## name of CamelContext +camel.springboot.name=sql-stored-connector + +# Oracle database configuration to use +# sql-stored-connector.url=jdbc:oracle:thin:@:: +# sql-stored-connector.user= +# sql-stored-connector.password= + +# Postgres database configuration +# sql-stored-connector.url=jdbc:postgresql://localhost:5432/test +# sql-stored-connector.user= +# sql-stored-connector.password= + +# Derby database configuration +sql-stored-connector.url=jdbc:derby:memory:testdb;create=true +sql-stored-connector.user=sa +sql-stored-connector.password="" \ No newline at end of file diff --git a/examples/sql-stored-example/src/main/resources/log4j2.properties b/examples/sql-stored-example/src/main/resources/log4j2.properties new file mode 100644 index 0000000..f83ffbe --- /dev/null +++ b/examples/sql-stored-example/src/main/resources/log4j2.properties @@ -0,0 +1,6 @@ +appender.out.type = Console +appender.out.name = out +appender.out.layout.type = PatternLayout +appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.file.ref = out