diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 29d7a0450..c096b6b27 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -28,6 +28,12 @@ jobs: with: cmake-version: '3.21.4' - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 - name: run-cppcheck run: | sudo apt install cppcheck @@ -98,84 +104,3 @@ jobs: with: name: linux64-deb-installer path: deb-installer - build-linux32: - runs-on: ubuntu-latest - steps: - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.11 - with: - cmake-version: '3.21.4' - - uses: actions/checkout@v2 - - name: run-cppcheck - run: | - sudo apt install cppcheck - sh run_cppcheck.sh - - name: upload-cppcheck-results - if: failure() - uses: actions/upload-artifact@v2 - with: - name: cppcheck-results - path: cppcheck-results.log - - name: get-dependencies - if: success() - run: | - # Need to install i386 versions - sudo dpkg --add-architecture i386 - sudo apt update - sudo apt install unixodbc:i386 unixodbc-dev:i386 odbcinst1debian2:i386 libodbc1:i386 libcurl4-openssl-dev:i386 libssl-dev:i386 uuid-dev:i386 cpp:i386 cpp-9:i386 gcc:i386 g++:i386 zlib1g-dev:i386 linux-headers-$(uname -r) gcc-multilib:i386 g++-multilib:i386 g++-9:i386 gcc-9:i386 gcc-9-multilib:i386 g++-9-multilib:i386 binutils:i386 make:i386 - - name: prepare-dsn - if: success() - run: | - sudo cp ./src/Tests/Tests/odbc-linux32.ini /etc/odbc.ini - sudo cp ./src/Tests/Tests/odbcinst-linux32.ini /etc/odbcinst.ini - mkdir -p ${{ github.workspace }}/odbc-logs - export ODBCSYSINI=/etc/ - export ODBCINSTINI=odbcinst.ini - export ODBCINI=/etc/odbc.ini - - name: configure-and-build-driver - if: success() - run: | - ./build_linux_release32_deb.sh - - name: run-tests - if: success() - run: | - ./build/odbc/bin/tests --gtest_output="xml:report.xml" - - name: prepare-test-results - if: always() - run: | - cp ${{ github.workspace }}/report.xml ${{ github.workspace }}/odbc-logs/ - - name: upload-test-report - if: failure() - uses: actions/upload-artifact@v2 - with: - name: test-results-linux32 - path: ${{ github.workspace }}/report.xml - - name: build-deb-installer - if: success() - run: | - cd cmake-build32 - cmake ../src - make -j4 - cpack . - cd .. - - name: create-output - if: success() - run: | - mkdir deb-installer - mkdir build-output - mkdir test-output - cp -v ./build/odbc/lib/*.a build-output/ - cp -v ./build/odbc/lib/*.so build-output/ - cp -v ./cmake-build32/*.deb deb-installer/ - - name: upload-build - if: success() - uses: actions/upload-artifact@v2 - with: - name: linux32-build - path: build-output - - name: upload-linux32-deb-installer - if: success() - uses: actions/upload-artifact@v2 - with: - name: linux32-deb-installer - path: deb-installer diff --git a/.github/workflows/mac-build.yml b/.github/workflows/mac-build.yml index 73a1926a7..a73fa8cbe 100644 --- a/.github/workflows/mac-build.yml +++ b/.github/workflows/mac-build.yml @@ -28,6 +28,12 @@ jobs: with: cmake-version: '3.21.4' - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 - name: run-cppcheck run: | brew install cppcheck diff --git a/.github/workflows/mac-debug-build.yml b/.github/workflows/mac-debug-build.yml index d145295b6..2f585aa95 100644 --- a/.github/workflows/mac-debug-build.yml +++ b/.github/workflows/mac-debug-build.yml @@ -24,6 +24,12 @@ jobs: with: cmake-version: '3.21.4' - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 - name: run-cppcheck run: | brew install cppcheck diff --git a/.github/workflows/release-workflow.yml b/.github/workflows/release-workflow.yml index a43764b51..941de7768 100644 --- a/.github/workflows/release-workflow.yml +++ b/.github/workflows/release-workflow.yml @@ -21,6 +21,12 @@ jobs: with: cmake-version: '3.21.4' - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 - name: run-cppcheck run: | brew install cppcheck @@ -72,6 +78,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x32 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.18.3 - name: Get specific version CMake, v3.18.3 uses: lukka/get-cmake@v3.18.3 - name: add-msbuild-to-path @@ -103,7 +118,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: Get specific version CMake, v3.18.3 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.18.3 uses: lukka/get-cmake@v3.18.3 - name: add-msbuild-to-path uses: microsoft/setup-msbuild@v1.0.2 diff --git a/.github/workflows/win-build.yml b/.github/workflows/win-build.yml index 58703ed69..1622747b9 100644 --- a/.github/workflows/win-build.yml +++ b/.github/workflows/win-build.yml @@ -24,6 +24,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x86 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.20.1 uses: lukka/get-cmake@v3.20.1 - name: add-msbuild-to-path @@ -75,6 +84,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.20.1 uses: lukka/get-cmake@v3.20.1 - name: add-msbuild-to-path @@ -120,6 +138,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.20.1 uses: lukka/get-cmake@v3.20.1 - name: add-msbuild-to-path diff --git a/.github/workflows/win-debug-build.yml b/.github/workflows/win-debug-build.yml index d35852160..a9ee6df98 100644 --- a/.github/workflows/win-debug-build.yml +++ b/.github/workflows/win-debug-build.yml @@ -17,6 +17,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x32 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.20.1 uses: lukka/get-cmake@v3.20.1 - name: add-msbuild-to-path @@ -61,6 +70,15 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 + - name: Get Java distribution + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '17' + architecture: x64 + - name: "Update path for Java" + run: | + echo "${{ env.JAVA_HOME }}\bin\server" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Get specific version CMake, v3.20.1 uses: lukka/get-cmake@v3.20.1 - name: add-msbuild-to-path diff --git a/src/odbcdriver/CMakeLists.txt b/src/odbcdriver/CMakeLists.txt index 46c198fe7..ae9a10508 100644 --- a/src/odbcdriver/CMakeLists.txt +++ b/src/odbcdriver/CMakeLists.txt @@ -15,6 +15,12 @@ project(odbcdriver) +# JNI requirements +find_package(Java REQUIRED) +find_package(JNI REQUIRED) +include(UseJava) +include_directories(${JNI_INCLUDE_DIRS} ${JAVA_INCLUDE_PATH2} ${JAVA_INCLUDE_PATH}) + # Source files for odbcdriver set(C_SOURCE_FILES bind.c columninfoclass.c @@ -72,6 +78,12 @@ set(CPP_SOURCE_FILES communication.cpp Row.cpp ScalarType.cpp Type.cpp + jni/JniEnv.cpp + jni/Connection.cpp + jni/ConnectionProperties.cpp + jni/JniEnv.h + jni/Connection.h + jni/ConnectionProperties.h ) if(WIN32) @@ -151,13 +163,16 @@ if(WIN32) target_link_libraries(odbcdriver wsock32 ws2_32 winmm user32 gdi32 legacy_stdio_definitions aws-cpp-sdk-core aws-cpp-sdk-sts kernel32 advapi32 secur32 XOleHlp Wldap32 crypt32 Normaliz odbccp32 odbc32) target_link_libraries(odbcdriver debug msvcrtd) target_link_libraries(odbcdriver optimized msvcrt) + target_link_libraries(odbcdriver ${JNI_LIBRARIES}) elseif(APPLE) # Apple specific target_link_libraries(odbcdriver iodbc iodbcinst aws-cpp-sdk-core aws-cpp-sdk-sts ) + target_link_libraries(odbcdriver ${JNI_LIBRARIES}) elseif(UNIX) # Unix specific include_directories(/usr/src/linux-headers-5.0.0-27/include) target_link_libraries(odbcdriver aws-cpp-sdk-core aws-cpp-sdk-sts odbc odbcinst) + target_link_libraries(odbcdriver ${JNI_LIBRARIES}) if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) if (Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/src/odbcdriver/jni/Connection.cpp b/src/odbcdriver/jni/Connection.cpp new file mode 100644 index 000000000..1d11c758e --- /dev/null +++ b/src/odbcdriver/jni/Connection.cpp @@ -0,0 +1,85 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#include +#include "Connection.h" +#include +using namespace std; + +namespace jni { + +void Connection::Connect() { + if (connection_ != nullptr) { + return; + } + + jni_env_->CreateJavaVM(); + JNIEnv* const env = jni_env_->GetJniEnv(); + static jclass cls_documentdb_connection = env->FindClass( + "software/amazon/documentdb/jdbc/" + "DocumentDbConnection"); // try to find the class + if (cls_documentdb_connection == nullptr) { + cerr << "ERROR: class not found !" << endl; + throw runtime_error("Class not found."); + } + cout << "DocumentDbConnection class found." << endl; + + static jmethodID m_documentdb_constructor = env->GetMethodID( + cls_documentdb_connection, "", + "(Lsoftware/amazon/documentdb/jdbc/DocumentDbConnectionProperties;)V"); + if (m_documentdb_constructor == nullptr) { + throw runtime_error( + "ERROR: constructor DocumentDbConnection(connectionProperties) not " + "found!"); + } + cout << "DocumentDbConnection constructor found." << endl; + + connection_ = + env->NewObject(cls_documentdb_connection, m_documentdb_constructor, + connection_properties_.GetHandle()); + if (connection_ == nullptr) { + throw runtime_error("Unable to construct DocumentDbConnection"); + } + cout << "Constructor created." << endl; +} + +Connection::~Connection() { + if (jni_env_ != nullptr && connection_ != nullptr) { + JNIEnv* const env = jni_env_->GetJniEnv(); + static jclass cls_documentdb_connection = env->FindClass( + "software/amazon/documentdb/jdbc/" + "DocumentDbConnection"); // try to find the class + if (cls_documentdb_connection == nullptr) { + cerr << "ERROR: class DocumentDbConnection not found !" << endl; + return; + } + cout << "DocumentDbConnection class found." << endl; + + static jmethodID m_dcoumentdb_close = + env->GetMethodID(cls_documentdb_connection, "doClose", "()V"); + if (m_dcoumentdb_close == nullptr) { + cerr << "ERROR: method DocumentDbConnection.doClose not found !" + << endl; + return; + } + env->CallVoidMethod(connection_, m_dcoumentdb_close); + // Ensure all member Java object are set to null. + connection_ = nullptr; + jni_env_ = nullptr; + } +} + +} // namespace jni diff --git a/src/odbcdriver/jni/Connection.h b/src/odbcdriver/jni/Connection.h new file mode 100644 index 000000000..7e8ba9411 --- /dev/null +++ b/src/odbcdriver/jni/Connection.h @@ -0,0 +1,46 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#pragma once + +#include +#include +#include "JniEnv.h" +#include "ConnectionProperties.h" + +namespace jni { +class Connection { + private: + JniEnv* jni_env_; + ConnectionProperties connection_properties_; + jobject connection_; + + public: + inline Connection(JniEnv* const jni_env, + ConnectionProperties connection_properties) + : jni_env_{jni_env}, + connection_properties_{connection_properties}, + connection_{nullptr} { + jni_env_->CreateJavaVM(); + } + + void Connect(); + inline jobject GetHandle() { + return connection_; + } + ~Connection(); +}; +} // namespace jni diff --git a/src/odbcdriver/jni/ConnectionProperties.cpp b/src/odbcdriver/jni/ConnectionProperties.cpp new file mode 100644 index 000000000..86491ab30 --- /dev/null +++ b/src/odbcdriver/jni/ConnectionProperties.cpp @@ -0,0 +1,87 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#include +#include +#include +#include "ConnectionProperties.h" + +using namespace std; + +namespace jni { + +std::shared_ptr< ConnectionProperties > ConnectionProperties::GetPropertiesFromConnectionString( + JniEnv* const jni_env_, const std::string connectionString) { + jni_env_->CreateJavaVM(); + JNIEnv* const env = jni_env_->GetJniEnv(); + static jclass cls_exception = env->FindClass("java/lang/Exception"); + static jmethodID m_exception_get_message = + env->GetMethodID(cls_exception, "getMessage", "()Ljava/lang/String;"); + + static jclass clsConnectionProperties = env->FindClass( + "software/amazon/documentdb/jdbc/" + "DocumentDbConnectionProperties"); // try to find the class + if (clsConnectionProperties == nullptr) { + cerr << "ERROR: class not found !" << endl; + throw runtime_error("Class not found."); + } + // if class found, continue + cout << "Class DocumentDbConnectionProperties found" << endl; + + jmethodID mGetPropertiesFromConnectionString = + GetMethodGetPropertiesFromConnectionString(env, + clsConnectionProperties); + if (mGetPropertiesFromConnectionString == nullptr) { + cerr << "ERROR: method void mymain() not found !" << endl; + throw runtime_error("Method not found."); + } + + jstring jstr = env->NewStringUTF(connectionString.c_str()); + jobject connection_properties = env->CallStaticObjectMethod( + clsConnectionProperties, mGetPropertiesFromConnectionString, + jstr); // call method + if (env->ExceptionCheck()) { + jstring o_message = (jstring)env->CallObjectMethod( + env->ExceptionOccurred(), m_exception_get_message); + jboolean isCopy; + const char* message = env->GetStringUTFChars(o_message, &isCopy); + env->ReleaseStringUTFChars(o_message, message); + throw runtime_error(message); + } + + return std::make_shared(connection_properties); +} + +jmethodID ConnectionProperties::GetMethodGetPropertiesFromConnectionString( + JNIEnv* const env, const jclass clsConnectionProperties) { + // find method + static jmethodID mGetPropertiesFromConnectionString = + env->GetStaticMethodID(clsConnectionProperties, + "getPropertiesFromConnectionString", + "(Ljava/lang/String;)" + "Lsoftware/amazon/documentdb/jdbc/" + "DocumentDbConnectionProperties;"); + if (mGetPropertiesFromConnectionString == nullptr) { + cerr << "ERROR: method void getPropertiesFromConnectionString() not " + "found !" + << endl; + return nullptr; + } else { + return mGetPropertiesFromConnectionString; + } +} + +} // namespace jni diff --git a/src/odbcdriver/jni/ConnectionProperties.h b/src/odbcdriver/jni/ConnectionProperties.h new file mode 100644 index 000000000..4b4780b8e --- /dev/null +++ b/src/odbcdriver/jni/ConnectionProperties.h @@ -0,0 +1,44 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#pragma once + +#include +#include +#include +#include "JniEnv.h" + +namespace jni { +class ConnectionProperties { + private: + jobject connection_properties_; + + static jmethodID GetMethodGetPropertiesFromConnectionString( + JNIEnv* const env, const jclass connection_properties); + + public: + static std::shared_ptr< ConnectionProperties > GetPropertiesFromConnectionString( + JniEnv* const jni_env, const std::string connectionString); + + inline ConnectionProperties(jobject connection_properties) + : connection_properties_{connection_properties} {}; + + inline jobject GetHandle() { + return connection_properties_; + }; +}; + +} // namespace jni diff --git a/src/odbcdriver/jni/JniEnv.cpp b/src/odbcdriver/jni/JniEnv.cpp new file mode 100644 index 000000000..b6f8c2ab3 --- /dev/null +++ b/src/odbcdriver/jni/JniEnv.cpp @@ -0,0 +1,63 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#include +#include +#include +#include +#include + +#include "JniEnv.h" + +using namespace std; +using namespace chrono; + +namespace jni { +JniEnv::JniEnv() { +} +void JniEnv::CreateJavaVM() { + if (jvm_ != nullptr && env_ != nullptr) { + return; + } + + int numOfOptions = 1; + JavaVMInitArgs vm_args = JavaVMInitArgs(); // Initialization arguments + JavaVMOption *options = + new JavaVMOption[numOfOptions]; // JVM invocation options + + // where to find java .class + options[0].optionString = + (char *)"-Djava.class.path=./documentdb-jdbc-1.0.0-all.jar"; + options[0].extraInfo = nullptr; + // minimum Java version + vm_args.version = JNI_VERSION_1_8; + // number of options + vm_args.nOptions = numOfOptions; + vm_args.options = options; + // invalid options make the JVM init fail + vm_args.ignoreUnrecognized = false; + + //=============== load and initialize + // Java VM and JNI interface ============= + jint rc = JNI_CreateJavaVM(&jvm_, (void **)&env_, &vm_args); // YES !! + delete[] options; // we then no longer need the initialisation options. + if (rc != JNI_OK) { + // TO DO: error processing... + throw std::runtime_error("Unable to create Java VM."); + } +} + +} // namespace jni diff --git a/src/odbcdriver/jni/JniEnv.h b/src/odbcdriver/jni/JniEnv.h new file mode 100644 index 000000000..a038bcc1b --- /dev/null +++ b/src/odbcdriver/jni/JniEnv.h @@ -0,0 +1,42 @@ +/* + * Copyright <2021> Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + */ + +#pragma once + +namespace jni { +#include + +class JniEnv { + private: + JavaVM *jvm_ = nullptr; + JNIEnv *env_ = nullptr; + + public: + JniEnv(); + void CreateJavaVM(); + inline JNIEnv* GetJniEnv() { + return env_; + }; + inline ~JniEnv() { + if (jvm_ != nullptr) { + jvm_->DestroyJavaVM(); + jvm_ = nullptr; + env_ = nullptr; + } + } +}; + +} // namespace jni