From 2a9fcc45a1d75912fb642ca2113bba3d259adb82 Mon Sep 17 00:00:00 2001 From: dalexandrov Date: Tue, 26 Jan 2021 16:49:00 +0200 Subject: [PATCH 1/3] Neo4j integration --- bom/pom.xml | 16 + .../io/helidon/common/FeatureCatalog.java | 17 + dependencies/pom.xml | 7 + .../integrations/neo4j/neo4j-mp/.dockerignore | 1 + .../integrations/neo4j/neo4j-mp/Dockerfile | 44 ++ .../neo4j/neo4j-mp/Dockerfile.jlink | 40 ++ .../neo4j/neo4j-mp/Dockerfile.native | 44 ++ .../integrations/neo4j/neo4j-mp/README.md | 167 +++++++ examples/integrations/neo4j/neo4j-mp/app.yaml | 50 ++ examples/integrations/neo4j/neo4j-mp/pom.xml | 110 +++++ .../integrations/neo4j/mp/Neo4jResource.java | 61 +++ .../integrations/neo4j/mp/domain/Actor.java | 41 ++ .../integrations/neo4j/mp/domain/Movie.java | 79 ++++ .../neo4j/mp/domain/MovieRepository.java | 77 ++++ .../integrations/neo4j/mp/domain/Person.java | 59 +++ .../neo4j/mp/domain/package-info.java | 19 + .../integrations/neo4j/mp/package-info.java | 20 + .../src/main/resources/META-INF/beans.xml | 26 ++ .../META-INF/microprofile-config.properties | 31 ++ .../src/main/resources/logging.properties | 27 ++ .../integrations/neo4j/mp/MainTest.java | 124 +++++ .../integrations/neo4j/mp/package-info.java | 20 + .../META-INF/microprofile-config.properties | 22 + .../integrations/neo4j/neo4j-se/.dockerignore | 1 + .../integrations/neo4j/neo4j-se/Dockerfile | 45 ++ .../neo4j/neo4j-se/Dockerfile.jlink | 40 ++ .../neo4j/neo4j-se/Dockerfile.native | 44 ++ .../integrations/neo4j/neo4j-se/README.md | 183 ++++++++ examples/integrations/neo4j/neo4j-se/app.yaml | 50 ++ examples/integrations/neo4j/neo4j-se/pom.xml | 124 +++++ .../examples/integrations/neo4j/se/Main.java | 142 ++++++ .../integrations/neo4j/se/MovieService.java | 54 +++ .../integrations/neo4j/se/domain/Actor.java | 50 ++ .../integrations/neo4j/se/domain/Movie.java | 79 ++++ .../neo4j/se/domain/MovieRepository.java | 75 +++ .../integrations/neo4j/se/domain/Person.java | 59 +++ .../neo4j/se/domain/package-info.java | 20 + .../integrations/neo4j/se/package-info.java | 23 + .../src/main/resources/application.yaml | 29 ++ .../src/main/resources/logging.properties | 34 ++ .../examples/quickstart/se/MainTest.java | 160 +++++++ .../examples/quickstart/se/package-info.java | 20 + examples/integrations/neo4j/pom.xml | 38 ++ examples/integrations/pom.xml | 1 + integrations/neo4j/health/pom.xml | 57 +++ .../neo4j/health/Neo4jHealthCheck.java | 120 +++++ .../neo4j/health/package-info.java | 20 + .../health/src/main/java/module-info.java | 30 ++ .../src/main/resources/META-INF/beans.xml | 25 + integrations/neo4j/helidon-neo4j-proposal.md | 62 +++ integrations/neo4j/metrics/pom.xml | 59 +++ .../metrics/Neo4jMetricsCdiExtension.java | 44 ++ .../neo4j/metrics/Neo4jMetricsSupport.java | 234 ++++++++++ .../neo4j/metrics/package-info.java | 20 + .../metrics/src/main/java/module-info.java | 39 ++ .../javax.enterprise.inject.spi.Extension | 17 + integrations/neo4j/neo4j/pom.xml | 66 +++ .../io/helidon/integrations/neo4j/Neo4j.java | 426 ++++++++++++++++++ .../integrations/neo4j/Neo4jCdiExtension.java | 61 +++ .../integrations/neo4j/Neo4jException.java | 41 ++ .../integrations/neo4j/package-info.java | 19 + .../neo4j/src/main/java/module-info.java | 34 ++ .../javax.enterprise.inject.spi.Extension | 17 + integrations/neo4j/pom.xml | 39 ++ integrations/pom.xml | 1 + 65 files changed, 3804 insertions(+) create mode 100644 examples/integrations/neo4j/neo4j-mp/.dockerignore create mode 100644 examples/integrations/neo4j/neo4j-mp/Dockerfile create mode 100644 examples/integrations/neo4j/neo4j-mp/Dockerfile.jlink create mode 100644 examples/integrations/neo4j/neo4j-mp/Dockerfile.native create mode 100644 examples/integrations/neo4j/neo4j-mp/README.md create mode 100644 examples/integrations/neo4j/neo4j-mp/app.yaml create mode 100644 examples/integrations/neo4j/neo4j-mp/pom.xml create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/Neo4jResource.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Actor.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Movie.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/MovieRepository.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Person.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/package-info.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/package-info.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/beans.xml create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/microprofile-config.properties create mode 100644 examples/integrations/neo4j/neo4j-mp/src/main/resources/logging.properties create mode 100644 examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/MainTest.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/package-info.java create mode 100644 examples/integrations/neo4j/neo4j-mp/src/test/resources/META-INF/microprofile-config.properties create mode 100644 examples/integrations/neo4j/neo4j-se/.dockerignore create mode 100644 examples/integrations/neo4j/neo4j-se/Dockerfile create mode 100644 examples/integrations/neo4j/neo4j-se/Dockerfile.jlink create mode 100644 examples/integrations/neo4j/neo4j-se/Dockerfile.native create mode 100644 examples/integrations/neo4j/neo4j-se/README.md create mode 100644 examples/integrations/neo4j/neo4j-se/app.yaml create mode 100644 examples/integrations/neo4j/neo4j-se/pom.xml create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/Main.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/MovieService.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Actor.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Movie.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/MovieRepository.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Person.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/package-info.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/package-info.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/resources/application.yaml create mode 100644 examples/integrations/neo4j/neo4j-se/src/main/resources/logging.properties create mode 100644 examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/MainTest.java create mode 100644 examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/package-info.java create mode 100644 examples/integrations/neo4j/pom.xml create mode 100644 integrations/neo4j/health/pom.xml create mode 100644 integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/Neo4jHealthCheck.java create mode 100644 integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/package-info.java create mode 100644 integrations/neo4j/health/src/main/java/module-info.java create mode 100644 integrations/neo4j/health/src/main/resources/META-INF/beans.xml create mode 100644 integrations/neo4j/helidon-neo4j-proposal.md create mode 100644 integrations/neo4j/metrics/pom.xml create mode 100644 integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsCdiExtension.java create mode 100644 integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsSupport.java create mode 100644 integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/package-info.java create mode 100644 integrations/neo4j/metrics/src/main/java/module-info.java create mode 100644 integrations/neo4j/metrics/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension create mode 100644 integrations/neo4j/neo4j/pom.xml create mode 100644 integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4j.java create mode 100644 integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jCdiExtension.java create mode 100644 integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jException.java create mode 100644 integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/package-info.java create mode 100644 integrations/neo4j/neo4j/src/main/java/module-info.java create mode 100644 integrations/neo4j/neo4j/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension create mode 100644 integrations/neo4j/pom.xml diff --git a/bom/pom.xml b/bom/pom.xml index 387001a7663..bd51cf7bdb7 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -904,6 +904,22 @@ helidon-logging-log4j ${helidon.version} + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j + ${helidon.version} + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-health + ${helidon.version} + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-metrics + ${helidon.version} + diff --git a/common/common/src/main/java/io/helidon/common/FeatureCatalog.java b/common/common/src/main/java/io/helidon/common/FeatureCatalog.java index fa23231a9bf..04cdfb39ad8 100644 --- a/common/common/src/main/java/io/helidon/common/FeatureCatalog.java +++ b/common/common/src/main/java/io/helidon/common/FeatureCatalog.java @@ -496,6 +496,23 @@ final class FeatureCatalog { "Zipkin", "Zipkin tracer integration", "Tracing", "Zipkin"); + add("io.helidon.integrations.neo4j", + FeatureDescriptor.builder() + .name("Neo4j integration") + .description("Integration with Neo4j driver") + .path("Neo4j") + .experimental(true) + .nativeSupported(true)); + add("io.helidon.integrations.neo4j.health", + FeatureDescriptor.builder() + .name("Neo4j Health") + .description("Health check for Neo4j integration") + .path("Neo4j", "Health")); + add("io.helidon.integrations.neo4j.metrics", + FeatureDescriptor.builder() + .name("Neo4j Metrics") + .description("Metrics for Neo4j integration") + .path("Neo4j", "Metrics")); add("io.helidon.webclient", FeatureDescriptor.builder() .name("Web Client") diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 0695cab8881..fc0ab70946f 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -106,6 +106,7 @@ 2.23.4 1.11.0 8.4.1.jre8 + 4.2.0 8.0.22 5.9.3.Final 4.1.58.Final @@ -1014,6 +1015,12 @@ micronaut-aop ${version.lib.micronaut} + + + org.neo4j.driver + neo4j-java-driver + ${version.lib.neo4j} + diff --git a/examples/integrations/neo4j/neo4j-mp/.dockerignore b/examples/integrations/neo4j/neo4j-mp/.dockerignore new file mode 100644 index 00000000000..c8b241f2215 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/.dockerignore @@ -0,0 +1 @@ +target/* \ No newline at end of file diff --git a/examples/integrations/neo4j/neo4j-mp/Dockerfile b/examples/integrations/neo4j/neo4j-mp/Dockerfile new file mode 100644 index 00000000000..e514bb51446 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/Dockerfile @@ -0,0 +1,44 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM maven:3.6-jdk-11 as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -DskipTests +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM openjdk:11-jre-slim +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-mp.jar ./ +COPY --from=build /helidon/target/libs ./libs + +CMD ["java", "-jar", "helidon-examples-integration-neo4j-mp.jar"] + +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-mp/Dockerfile.jlink b/examples/integrations/neo4j/neo4j-mp/Dockerfile.jlink new file mode 100644 index 00000000000..3ee0307a2a5 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/Dockerfile.jlink @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM maven:3.6.3-jdk-11-slim as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build to create the custom Java Runtime Image +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn -Ddocker.build=true package -Pjlink-image -DskipTests +RUN echo "done!" + +# 2nd stage, build the final image with the JRI built in the 1st stage + +FROM debian:stretch-slim +WORKDIR /helidon +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-mp-jri ./ +ENTRYPOINT ["/bin/bash", "/helidon/bin/start"] +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-mp/Dockerfile.native b/examples/integrations/neo4j/neo4j-mp/Dockerfile.native new file mode 100644 index 00000000000..e5d9ad92c37 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/Dockerfile.native @@ -0,0 +1,44 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM helidon/jdk11-graalvm-maven:20.2.0 as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM scratch +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-mp . + +ENTRYPOINT ["./helidon-examples-integration-neo4j-mp"] + +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-mp/README.md b/examples/integrations/neo4j/neo4j-mp/README.md new file mode 100644 index 00000000000..ea20d93a7a7 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/README.md @@ -0,0 +1,167 @@ +# Helidon Quickstart MP Example + +This example implements a simple Neo4j REST service using MicroProfile. + +## Build and run + +Bring up a Neo4j instance via Docker + +```bash +docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:4.0 +``` + +Goto the Neo4j browser and play the first step of the movies graph: [`:play movies`](http://localhost:7474/browser/?cmd=play&arg=movies). + + +Then build with JDK11+ +```bash +mvn package +java -jar target/helidon-integrations-neo4j-mp.jar +``` + +## Exercise the application + +``` +curl -X GET http://localhost:8080/movies + +``` + +## Try health and metrics + +``` +curl -s -X GET http://localhost:8080/health +{"outcome":"UP",... +. . . + +# Prometheus Format +curl -s -X GET http://localhost:8080/metrics +# TYPE base:gc_g1_young_generation_count gauge +. . . + +# JSON Format +curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics +{"base":... +. . . + +``` + +## Build the Docker Image + +``` +docker build -t helidon-integrations-neo4j-mp . +``` + +## Start the application with Docker + +``` +docker run --rm -p 8080:8080 helidon-integrations-neo4j-mp:latest +``` + +Exercise the application as described above + +## Deploy the application to Kubernetes + +``` +kubectl cluster-info # Verify which cluster +kubectl get pods # Verify connectivity to cluster +kubectl create -f app.yaml # Deploy application +kubectl get service helidon-integrations-neo4j-mp # Verify deployed service +``` + +## Build a native image with GraalVM + +GraalVM allows you to compile your programs ahead-of-time into a native + executable. See https://www.graalvm.org/docs/reference-manual/aot-compilation/ + for more information. + +You can build a native executable in 2 different ways: +* With a local installation of GraalVM +* Using Docker + +### Local build + +Download Graal VM at https://www.graalvm.org/downloads. We recommend +version `20.1.0` or later. + +``` +# Setup the environment +export GRAALVM_HOME=/path +# build the native executable +mvn package -Pnative-image +``` + +You can also put the Graal VM `bin` directory in your PATH, or pass + `-DgraalVMHome=/path` to the Maven command. + +See https://github.com/oracle/helidon-build-tools/tree/master/helidon-maven-plugin#goal-native-image + for more information. + +Start the application: + +``` +./target/helidon-quickstart-mp +``` + +### Multi-stage Docker build + +Build the "native" Docker Image + +``` +docker build -t helidon-integrations-neo4j-mp-native -f Dockerfile.native . +``` + +Start the application: + +``` +docker run --rm -p 8080:8080 helidon-integrations-neo4j-mp-native:latest +``` + + +## Build a Java Runtime Image using jlink + +You can build a custom Java Runtime Image (JRI) containing the application jars and the JDK modules +on which they depend. This image also: + +* Enables Class Data Sharing by default to reduce startup time. +* Contains a customized `start` script to simplify CDS usage and support debug and test modes. + +You can build a custom JRI in two different ways: +* Local +* Using Docker + + +### Local build + +``` +# build the JRI +mvn package -Pjlink-image +``` + +See https://github.com/oracle/helidon-build-tools/tree/master/helidon-maven-plugin#goal-jlink-image + for more information. + +Start the application: + +``` +./target/helidon-integrations-neo4j-mp-jri/bin/start +``` + +### Multi-stage Docker build + +Build the JRI as a Docker Image + +``` +docker build -t helidon-integrations-neo4j-mp-jri -f Dockerfile.jlink . +``` + +Start the application: + +``` +docker run --rm -p 8080:8080 helidon-integrations-neo4j-mp-jri:latest +``` + +See the start script help: + +``` +docker run --rm helidon-integrations-neo4j-mp-jri:latest --help +``` diff --git a/examples/integrations/neo4j/neo4j-mp/app.yaml b/examples/integrations/neo4j/neo4j-mp/app.yaml new file mode 100644 index 00000000000..3359b178eef --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/app.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +kind: Service +apiVersion: v1 +metadata: + name: helidon-examples-integration-neo4j-mp + labels: + app: helidon-examples-integration-neo4j-mp +spec: + type: NodePort + selector: + app: helidon-examples-integration-neo4j-mp + ports: + - port: 8080 + targetPort: 8080 + name: http +--- +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: helidon-examples-integration-neo4j-mp +spec: + replicas: 1 + template: + metadata: + labels: + app: helidon-examples-integration-neo4j-mp + version: v1 + spec: + containers: + - name: helidon-examples-integration-neo4j-mp + image: helidon-examples-integration-neo4j-mp + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 +--- diff --git a/examples/integrations/neo4j/neo4j-mp/pom.xml b/examples/integrations/neo4j/neo4j-mp/pom.xml new file mode 100644 index 00000000000..09e80be6e3e --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/pom.xml @@ -0,0 +1,110 @@ + + + + + 4.0.0 + + io.helidon.applications + helidon-mp + 2.2.1-SNAPSHOT + ../../../../applications/mp/pom.xml + + io.helidon.examples.integrations.neo4j + helidon-examples-integration-neo4j-mp + Helidon Neo4j MP integration Example + + + 4.2.1 + + + + + io.helidon.microprofile.bundles + helidon-microprofile + + + org.neo4j.driver + neo4j-java-driver + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-metrics + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-health + + + + org.jboss + jandex + runtime + true + + + + + + org.neo4j.test + neo4j-harness + ${neo4j-harness.version} + test + + + org.slf4j + slf4j-nop + + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + + + + + diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/Neo4jResource.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/Neo4jResource.java new file mode 100644 index 00000000000..335b808ea26 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/Neo4jResource.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp; + +import java.util.List; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import io.helidon.examples.integrations.neo4j.mp.domain.Movie; +import io.helidon.examples.integrations.neo4j.mp.domain.MovieRepository; + +@Path("/movies") +@RequestScoped +public class Neo4jResource { + /** + * The greeting message provider. + */ + private final MovieRepository movieRepository; + + /** + * Constructor. + * + * @param movieRepository + */ + @Inject + public Neo4jResource(MovieRepository movieRepository) { + this.movieRepository = movieRepository; + } + + /** + * All movies. + * + * @return json String with all movies + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public List getAllMovies() { + return movieRepository.findAll(); + } + +} + diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Actor.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Actor.java new file mode 100644 index 00000000000..36576bb7599 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Actor.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Actor class. + */ +public class Actor { + + private final String name; + + private final List roles; + + /** + * Constructor. + * + * @param name + * @param roles + */ + public Actor(String name, final List roles) { + this.name = name; + this.roles = new ArrayList<>(roles); + } +} diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Movie.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Movie.java new file mode 100644 index 00000000000..dd8e302b04c --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Movie.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Movie class. + */ +public class Movie { + + private final String title; + + private final String description; + + private List actors = new ArrayList<>(); + + private List directors = new ArrayList<>(); + + private Integer released; + + /** + * Constructor. + * + * @param title + * @param description + */ + public Movie(String title, String description) { + this.title = title; + this.description = description; + } + + public String getTitle() { + return title; + } + + public List getActors() { + return actors; + } + + public void setActors(List actors) { + this.actors = actors; + } + + public String getDescription() { + return description; + } + + public List getDirectors() { + return directors; + } + + public void setDirectorss(List directors) { + this.directors = directors; + } + + public Integer getReleased() { + return released; + } + + public void setReleased(Integer released) { + this.released = released; + } +} diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/MovieRepository.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/MovieRepository.java new file mode 100644 index 00000000000..f3fae7d6589 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/MovieRepository.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp.domain; + +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Value; +/** + * The Movies repository. + */ +@ApplicationScoped +public class MovieRepository { + + private final Driver driver; + + /** + * Constructor. + * @param driver + */ + @Inject + public MovieRepository(Driver driver) { + this.driver = driver; + } + + /** + * Return al Movies. + * @return list with movies + */ + public List findAll() { + + try (var session = driver.session()) { + + var query = "" + + "match (m:Movie) " + + "match (m) <- [:DIRECTED] - (d:Person) " + + "match (m) <- [r:ACTED_IN] - (a:Person) " + + "return m, collect(d) as directors, collect({name:a.name, roles: r.roles}) as actors"; + + return session.readTransaction(tx -> tx.run(query).list(r -> { + var movieNode = r.get("m").asNode(); + + var directors = r.get("directors").asList(v -> { + var personNode = v.asNode(); + return new Person(personNode.get("born").asInt(), personNode.get("name").asString()); + }); + + var actors = r.get("actors").asList(v -> { + return new Actor(v.get("name").asString(), v.get("roles").asList(Value::asString)); + }); + + var m = new Movie(movieNode.get("title").asString(), movieNode.get("tagline").asString()); + m.setReleased(movieNode.get("released").asInt()); + m.setDirectorss(directors); + m.setActors(actors); + return m; + })); + } + } +} diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Person.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Person.java new file mode 100644 index 00000000000..19854eab177 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/Person.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp.domain; + +/** + * The Person class. + */ +public class Person { + + private final String name; + + private Integer born; + + /** + * Person constructor. + * + * @param born + * @param name + */ + public Person(Integer born, String name) { + this.born = born; + this.name = name; + } + + public String getName() { + return name; + } + + public Integer getBorn() { + return born; + } + + public void setBorn(Integer born) { + this.born = born; + } + + @SuppressWarnings("checkstyle:OperatorWrap") + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", born=" + born + + '}'; + } +} diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/package-info.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/package-info.java new file mode 100644 index 00000000000..561b2665070 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/domain/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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. + */ +/** + * Domain objects for movie DB. + */ +package io.helidon.examples.integrations.neo4j.mp.domain; diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/package-info.java b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/package-info.java new file mode 100644 index 00000000000..d78d418a922 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/java/io/helidon/examples/integrations/neo4j/mp/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Resources. + */ +package io.helidon.examples.integrations.neo4j.mp; diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/beans.xml b/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..1c90af4ecb3 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/microprofile-config.properties b/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..70f63d64091 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,31 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# Application properties. This is the default greeting +app.greeting=Hello + +# Microprofile server properties +server.port=8080 +server.host=0.0.0.0 + +# Enable the optional MicroProfile Metrics REST.request metrics +metrics.rest-request.enabled=true + +# Neo4j settings +neo4j.uri=bolt://localhost:7687 +neo4j.authentication.username=neo4j +neo4j.authentication.password: secret +neo4j.pool.metricsEnabled: true diff --git a/examples/integrations/neo4j/neo4j-mp/src/main/resources/logging.properties b/examples/integrations/neo4j/neo4j-mp/src/main/resources/logging.properties new file mode 100644 index 00000000000..b5cf2f64627 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/main/resources/logging.properties @@ -0,0 +1,27 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.common.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO diff --git a/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/MainTest.java b/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/MainTest.java new file mode 100644 index 00000000000..88252cdb7e1 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/MainTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.mp; + +import javax.enterprise.inject.se.SeContainer; +import javax.enterprise.inject.spi.CDI; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; + +import io.helidon.microprofile.server.Server; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.neo4j.harness.Neo4j; +import org.neo4j.harness.Neo4jBuilders; + +/** + * Main tests of the application done here. + */ +class MainTest { + private static Server server; + private static Neo4j embeddedDatabaseServer; + + @BeforeAll + public static void startTheServer() throws Exception { + + embeddedDatabaseServer = Neo4jBuilders.newInProcessBuilder() + .withDisabledServer() + .withFixture(FIXTURE) + .build(); + + System.setProperty("neo4j.uri", embeddedDatabaseServer.boltURI().toString()); + + server = Server.create().start(); + + } + + @AfterAll + static void destroyClass() { + CDI current = CDI.current(); + ((SeContainer) current).close(); + embeddedDatabaseServer.close(); + } + + + @Test + void testMovies() { + + Client client = ClientBuilder.newClient(); + + JsonArray jsorArray = client + .target(getConnectionString("/movies")) + .request() + .get(JsonArray.class); + JsonObject first = jsorArray.getJsonObject(0); + Assertions.assertEquals("The Matrix", first.getString("title")); + + } + + private String getConnectionString(String path) { + return "http://localhost:" + server.port() + path; + } + + static final String FIXTURE = "" + + "CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'})\n" + + "CREATE (Keanu:Person {name:'Keanu Reeves', born:1964})\n" + + "CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967})\n" + + "CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961})\n" + + "CREATE (Hugo:Person {name:'Hugo Weaving', born:1960})\n" + + "CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967})\n" + + "CREATE (LanaW:Person {name:'Lana Wachowski', born:1965})\n" + + "CREATE (JoelS:Person {name:'Joel Silver', born:1952})\n" + + "CREATE (KevinB:Person {name:'Kevin Bacon', born:1958})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrix),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrix),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrix)\n" + + "\n" + + "CREATE (Emil:Person {name:\"Emil Eifrem\", born:1978})\n" + + "CREATE (Emil)-[:ACTED_IN {roles:[\"Emil\"]}]->(TheMatrix)\n" + + "\n" + + "CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrixReloaded),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrixReloaded),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrixReloaded)\n" + + "\n" + + "CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions),\n" + + "(KevinB)-[:ACTED_IN {roles:['Unknown']}]->(TheMatrixRevolutions),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrixRevolutions),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrixRevolutions),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrixRevolutions)\n"; +} diff --git a/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/package-info.java b/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/package-info.java new file mode 100644 index 00000000000..acbfc3cde79 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/test/java/io/helidon/examples/integrations/neo4j/mp/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Tests for MP Neo4j app. + */ +package io.helidon.examples.integrations.neo4j.mp; \ No newline at end of file diff --git a/examples/integrations/neo4j/neo4j-mp/src/test/resources/META-INF/microprofile-config.properties b/examples/integrations/neo4j/neo4j-mp/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..0f7f7a9d224 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-mp/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,22 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + + +# Override configuration to use a random port for the unit tests +config_ordinal=1000 +# Microprofile server properties +server.port=-1 +server.host=0.0.0.0 diff --git a/examples/integrations/neo4j/neo4j-se/.dockerignore b/examples/integrations/neo4j/neo4j-se/.dockerignore new file mode 100644 index 00000000000..c8b241f2215 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/.dockerignore @@ -0,0 +1 @@ +target/* \ No newline at end of file diff --git a/examples/integrations/neo4j/neo4j-se/Dockerfile b/examples/integrations/neo4j/neo4j-se/Dockerfile new file mode 100644 index 00000000000..ba11b50163d --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/Dockerfile @@ -0,0 +1,45 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM maven:3.6-jdk-11 as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM openjdk:11-jre-slim +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-se.jar ./ +COPY --from=build /helidon/target/libs ./libs + +CMD ["java", "-jar", "helidon-examples-integration-neo4j-se.jar"] + +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-se/Dockerfile.jlink b/examples/integrations/neo4j/neo4j-se/Dockerfile.jlink new file mode 100644 index 00000000000..b514fa72c1e --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/Dockerfile.jlink @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM maven:3.6.3-jdk-11-slim as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build to create the custom Java Runtime Image +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn -Ddocker.build=true package -Pjlink-image -DskipTests +RUN echo "done!" + +# 2nd stage, build the final image with the JRI built in the 1st stage + +FROM debian:stretch-slim +WORKDIR /helidon +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-se-jri ./ +ENTRYPOINT ["/bin/bash", "/helidon/bin/start"] +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-se/Dockerfile.native b/examples/integrations/neo4j/neo4j-se/Dockerfile.native new file mode 100644 index 00000000000..c745d8736d5 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/Dockerfile.native @@ -0,0 +1,44 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# 1st stage, build the app +FROM helidon/jdk11-graalvm-maven:20.2.0 as build + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM scratch +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/helidon-examples-integration-neo4j-se . + +ENTRYPOINT ["./helidon-examples-integration-neo4j-se"] + +EXPOSE 8080 diff --git a/examples/integrations/neo4j/neo4j-se/README.md b/examples/integrations/neo4j/neo4j-se/README.md new file mode 100644 index 00000000000..f250d1a8d5f --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/README.md @@ -0,0 +1,183 @@ +# Helidon SE integration with Neo4J example + +## Build and run + +Bring up a Neo4j instance via Docker + +```bash +docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:4.0 +``` + +Goto the Neo4j browser and play the first step of the movies graph: [`:play movies`](http://localhost:7474/browser/?cmd=play&arg=movies). + +Build and run with With JDK11+ +```bash +mvn package +java -jar target/helidon-integrations-neo4j-se.jar +``` + +Then access the rest API like this: + +```` +curl localhost:8080/api/movies +```` + +#Health and metrics + +Heo4jSupport provides health checks and metrics reading from Neo4j. + +To enable them add to routing: +```java +// metrics +Neo4jMetricsSupport.builder() + .driver(neo4j.driver()) + .build() + .initialize(); +// health checks +HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks + .addReadiness(Neo4jHealthCheck.create(neo4j.driver())) + .build(); + +return Routing.builder() + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics" + .register(movieService) + .build(); +``` +and enable them in the driver: +```yaml + pool: + metricsEnabled: true +``` + + +```` +curl localhost:8080/health +```` + +```` +curl localhost:8080/metrics +```` + + + +## Build the Docker Image + +``` +docker build -t helidon-integrations-heo4j-se . +``` + +## Start the application with Docker + +``` +docker run --rm -p 8080:8080 helidon-integrations-heo4j-se:latest +``` + +Exercise the application as described above + +## Deploy the application to Kubernetes + +``` +kubectl cluster-info # Verify which cluster +kubectl get pods # Verify connectivity to cluster +kubectl create -f app.yaml # Deply application +kubectl get service helidon-integrations-heo4j-se # Get service info +``` + +## Build a native image with GraalVM + +GraalVM allows you to compile your programs ahead-of-time into a native + executable. See https://www.graalvm.org/docs/reference-manual/aot-compilation/ + for more information. + +You can build a native executable in 2 different ways: +* With a local installation of GraalVM +* Using Docker + +### Local build + +Download Graal VM at https://www.graalvm.org/downloads. We recommend +version `20.1.0` or later. + +``` +# Setup the environment +export GRAALVM_HOME=/path +# build the native executable +mvn package -Pnative-image +``` + +You can also put the Graal VM `bin` directory in your PATH, or pass + `-DgraalVMHome=/path` to the Maven command. + +See https://github.com/oracle/helidon-build-tools/tree/master/helidon-maven-plugin#goal-native-image + for more information. + +Start the application: + +``` +./target/helidon-integrations-heo4j-se +``` + +### Multi-stage Docker build + +Build the "native" Docker Image + +``` +docker build -t helidon-integrations-heo4j-se-native -f Dockerfile.native . +``` + +Start the application: + +``` +docker run --rm -p 8080:8080 helidon-integrations-heo4j-se-native:latest +``` + +## Build a Java Runtime Image using jlink + +You can build a custom Java Runtime Image (JRI) containing the application jars and the JDK modules +on which they depend. This image also: + +* Enables Class Data Sharing by default to reduce startup time. +* Contains a customized `start` script to simplify CDS usage and support debug and test modes. + +You can build a custom JRI in two different ways: +* Local +* Using Docker + + +### Local build + +``` +# build the JRI +mvn package -Pjlink-image +``` + +See https://github.com/oracle/helidon-build-tools/tree/master/helidon-maven-plugin#goal-jlink-image + for more information. + +Start the application: + +``` +./target/helidon-integrations-heo4j-se-jri/bin/start +``` + +### Multi-stage Docker build + +Build the JRI as a Docker Image + +``` +docker build -t helidon-integrations-heo4j-se-jri -f Dockerfile.jlink . +``` + +Start the application: + +``` +docker run --rm -p 8080:8080 helidon-integrations-heo4j-se-jri:latest +``` + +See the start script help: + +``` +docker run --rm helidon-integrations-heo4j-se-jri:latest --help +``` diff --git a/examples/integrations/neo4j/neo4j-se/app.yaml b/examples/integrations/neo4j/neo4j-se/app.yaml new file mode 100644 index 00000000000..7028ee6a53a --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/app.yaml @@ -0,0 +1,50 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +kind: Service +apiVersion: v1 +metadata: + name: helidon-examples-integration-neo4j-se + labels: + app: helidon-examples-integration-neo4j-se +spec: + type: NodePort + selector: + app: helidon-examples-integration-neo4j-se + ports: + - port: 8080 + targetPort: 8080 + name: http +--- +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: helidon-examples-integration-neo4j-se +spec: + replicas: 1 + template: + metadata: + labels: + app: helidon-examples-integration-neo4j-se + version: v1 + spec: + containers: + - name: helidon-examples-integration-neo4j-se + image: helidon-examples-integration-neo4j-se + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 +--- diff --git a/examples/integrations/neo4j/neo4j-se/pom.xml b/examples/integrations/neo4j/neo4j-se/pom.xml new file mode 100644 index 00000000000..c5e20b38bdf --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/pom.xml @@ -0,0 +1,124 @@ + + + + + 4.0.0 + + io.helidon.applications + helidon-se + 2.2.1-SNAPSHOT + ../../../../applications/se/pom.xml + + io.helidon.examples.integrations.neo4j + helidon-examples-integration-neo4j-se + 2.2.1-SNAPSHOT + Helidon Integrations Neo4j SE Example + + + io.helidon.examples.integrations.neo4j.se.Main + 1.15.0 + 4.2.1 + + + + + org.neo4j.driver + neo4j-java-driver + + + io.helidon.webserver + helidon-webserver + + + io.helidon.media + helidon-media-jsonp + + + io.helidon.media + helidon-media-jsonb + + + io.helidon.config + helidon-config-yaml + + + io.helidon.health + helidon-health + + + io.helidon.health + helidon-health-checks + + + io.helidon.metrics + helidon-metrics + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-metrics + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-health + + + org.junit.jupiter + junit-jupiter-api + test + + + io.helidon.webclient + helidon-webclient + test + + + + org.neo4j.test + neo4j-harness + ${neo4j-harness.version} + test + + + org.slf4j + slf4j-nop + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + + diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/Main.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/Main.java new file mode 100644 index 00000000000..16bdaacb6c0 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/Main.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; + +import io.helidon.common.LogConfig; +import io.helidon.config.Config; +import io.helidon.examples.integrations.neo4j.se.domain.MovieRepository; +import io.helidon.health.HealthSupport; +import io.helidon.health.checks.HealthChecks; +import io.helidon.integrations.neo4j.Neo4j; +import io.helidon.integrations.neo4j.health.Neo4jHealthCheck; +import io.helidon.integrations.neo4j.metrics.Neo4jMetricsSupport; +import io.helidon.media.jsonb.JsonbSupport; +import io.helidon.media.jsonp.JsonpSupport; +import io.helidon.metrics.MetricsSupport; +import io.helidon.webserver.Routing; +import io.helidon.webserver.WebServer; + +import org.neo4j.driver.Driver; + +/** + * The application main class. + */ +public final class Main { + + /** + * Cannot be instantiated. + */ + private Main() { + } + + /** + * Application main entry point. + * @param args command line arguments. + * @throws IOException if there are problems reading logging properties + */ + public static void main(final String[] args) throws IOException { + startServer(); + } + + /** + * Start the server. + * @return the created WebServer instance + * @throws IOException if there are problems reading logging properties + */ + public static WebServer startServer() throws IOException { + // load logging configuration + LogConfig.configureRuntime(); + + // By default this will pick up application.yaml from the classpath + Config config = Config.create(); + + WebServer server = WebServer.builder(createRouting(config)) + .config(config.get("server")) + .addMediaSupport(JsonpSupport.create()) + .addMediaSupport(JsonbSupport.create()) + .build(); + + // Try to start the server. If successful, print some info and arrange to + // print a message at shutdown. If unsuccessful, print the exception. + server.start() + .thenAccept(ws -> { + System.out.println( + "WEB server is up! http://localhost:" + ws.port() + "/api/movies"); + ws.whenShutdown().thenRun(() + -> System.out.println("WEB server is DOWN. Good bye!")); + }) + .exceptionally(t -> { + System.err.println("Startup failed: " + t.getMessage()); + t.printStackTrace(System.err); + return null; + }); + + // Server threads are not daemon. No need to block. Just react. + + return server; + } + + /** + * Creates new Routing. + * + * @return routing configured with JSON support, a health check, and a service + * @param config configuration of this server + */ + private static Routing createRouting(Config config) { + + MetricsSupport metrics = MetricsSupport.create(); + + Neo4j neo4j = Neo4j.create(config.get("neo4j")); + + // registers all metrics + Neo4jMetricsSupport.builder() + .driver(neo4j.driver()) + .build() + .initialize(); + + Neo4jHealthCheck healthCheck = Neo4jHealthCheck.create(neo4j.driver()); + + Driver neo4jDriver = neo4j.driver(); + + MovieService movieService = new MovieService(new MovieRepository(neo4jDriver)); + + HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks + .addReadiness(healthCheck) + .build(); + + return Routing.builder() + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics" + .register(movieService) + .build(); + } + + /** + * Configure logging from logging.properties file. + */ + private static void setupLogging() throws IOException { + try (InputStream is = Main.class.getResourceAsStream("/logging.properties")) { + LogManager.getLogManager().readConfiguration(is); + } + } + +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/MovieService.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/MovieService.java new file mode 100644 index 00000000000..6e010556b4b --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/MovieService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se; + +import io.helidon.examples.integrations.neo4j.se.domain.MovieRepository; +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +/** + * The Movie service. + * + */ +public class MovieService implements Service { + + private final MovieRepository movieRepository; + + /** + * The movies service. + * @param movieRepository + */ + public MovieService(MovieRepository movieRepository) { + this.movieRepository = movieRepository; + } + + /** + * Main routing done here. + * + * @param rules + */ + @Override + public void update(Routing.Rules rules) { + rules.get("/api/movies", this::findMoviesHandler); + } + + private void findMoviesHandler(ServerRequest request, ServerResponse response) { + response.send(this.movieRepository.findAll()); + } +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Actor.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Actor.java new file mode 100644 index 00000000000..b417735a2e0 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Actor.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Actor class. + */ +public class Actor { + + private final String name; + + private final List roles; + + /** + * Constructor for actor. + * + * @param name + * @param roles + */ + public Actor(String name, final List roles) { + this.name = name; + this.roles = new ArrayList<>(roles); + } + + public String getName() { + return name; + } + + public List getRoles() { + return roles; + } +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Movie.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Movie.java new file mode 100644 index 00000000000..ab905bb6908 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Movie.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Movie class. + */ +public class Movie { + + private final String title; + + private final String description; + + private List actors = new ArrayList<>(); + + private List directors = new ArrayList<>(); + + private Integer released; + + /** + * Constructor for Movie. + * + * @param title + * @param description + */ + public Movie(String title, String description) { + this.title = title; + this.description = description; + } + + public String getTitle() { + return title; + } + + public List getActors() { + return actors; + } + + public void setActors(List actors) { + this.actors = actors; + } + + public String getDescription() { + return description; + } + + public List getDirectors() { + return directors; + } + + public void setDirectorss(List directors) { + this.directors = directors; + } + + public Integer getReleased() { + return released; + } + + public void setReleased(Integer released) { + this.released = released; + } +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/MovieRepository.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/MovieRepository.java new file mode 100644 index 00000000000..711715e1313 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/MovieRepository.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se.domain; + +import java.util.List; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Value; + + +/** + * The Movie repository. + */ +public final class MovieRepository { + + private final Driver driver; + + /** + * Constructor for the repo. + * + * @param driver + */ + public MovieRepository(Driver driver) { + this.driver = driver; + } + + /** + * Returns all the movies. + * @return List with movies + */ + public List findAll(){ + + try (var session = driver.session()) { + + var query = "" + + "match (m:Movie) " + + "match (m) <- [:DIRECTED] - (d:Person) " + + "match (m) <- [r:ACTED_IN] - (a:Person) " + + "return m, collect(d) as directors, collect({name:a.name, roles: r.roles}) as actors"; + + return session.readTransaction(tx -> tx.run(query).list(r -> { + var movieNode = r.get("m").asNode(); + + var directors = r.get("directors").asList(v -> { + var personNode = v.asNode(); + return new Person(personNode.get("born").asInt(), personNode.get("name").asString()); + }); + + var actors = r.get("actors").asList(v -> { + return new Actor(v.get("name").asString(), v.get("roles").asList(Value::asString)); + }); + + var m = new Movie(movieNode.get("title").asString(), movieNode.get("tagline").asString()); + m.setReleased(movieNode.get("released").asInt()); + m.setDirectorss(directors); + m.setActors(actors); + return m; + })); + } + } +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Person.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Person.java new file mode 100644 index 00000000000..5cbf8344462 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/Person.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.examples.integrations.neo4j.se.domain; + + +/** + * The Person class. + */ +public class Person { + + private final String name; + + private Integer born; + + /** + * Constrictor for person. + * @param born + * @param name + */ + public Person(Integer born, String name) { + this.born = born; + this.name = name; + } + + public String getName() { + return name; + } + + public Integer getBorn() { + return born; + } + + public void setBorn(Integer born) { + this.born = born; + } + + @SuppressWarnings("checkstyle:OperatorWrap") + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", born=" + born + + '}'; + } +} diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/package-info.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/package-info.java new file mode 100644 index 00000000000..da6c8cc1d6e --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/domain/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Domain objects for movies. + */ +package io.helidon.examples.integrations.neo4j.se.domain; diff --git a/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/package-info.java b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/package-info.java new file mode 100644 index 00000000000..62a2ff870ee --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/java/io/helidon/examples/integrations/neo4j/se/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * SE Neo4j demo application. + *

+ * + * @see io.helidon.examples.integrations.neo4j.se.Main + */ +package io.helidon.examples.integrations.neo4j.se; diff --git a/examples/integrations/neo4j/neo4j-se/src/main/resources/application.yaml b/examples/integrations/neo4j/neo4j-se/src/main/resources/application.yaml new file mode 100644 index 00000000000..9103d052e4f --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/resources/application.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + + +server: + port: 8080 + host: 0.0.0.0 + + +neo4j: + uri: bolt://localhost:7687 + authentication: + username: neo4j + password: secret + pool: + metricsEnabled: true diff --git a/examples/integrations/neo4j/neo4j-se/src/main/resources/logging.properties b/examples/integrations/neo4j/neo4j-se/src/main/resources/logging.properties new file mode 100644 index 00000000000..aced7e48602 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/main/resources/logging.properties @@ -0,0 +1,34 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.common.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Component specific log levels +#io.helidon.webserver.level=INFO +#io.helidon.config.level=INFO +#io.helidon.security.level=INFO +#io.helidon.common.level=INFO +#io.netty.level=INFO diff --git a/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/MainTest.java b/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/MainTest.java new file mode 100644 index 00000000000..7b7889c7bc0 --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/MainTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.examples.quickstart.se; + +import java.util.concurrent.TimeUnit; + +import javax.json.JsonArray; + +import io.helidon.examples.integrations.neo4j.se.Main; +import io.helidon.media.jsonp.JsonpSupport; +import io.helidon.webclient.WebClient; +import io.helidon.webserver.WebServer; + + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.neo4j.harness.Neo4j; +import org.neo4j.harness.Neo4jBuilders; + +/** + * Main test class for Neo4j Helidon SE quickstarter. + */ +public class MainTest { + + private static WebServer webServer; + private static WebClient webClient; + + private static Neo4j embeddedDatabaseServer; + + @BeforeAll + private static void startTheServer() throws Exception { + + + embeddedDatabaseServer = Neo4jBuilders.newInProcessBuilder() + .withDisabledServer() + .withFixture(FIXTURE) + .build(); + + System.setProperty("neo4j.uri", embeddedDatabaseServer.boltURI().toString()); + + webServer = Main.startServer(); + + long timeout = 2000; // 2 seconds should be enough to start the server + long now = System.currentTimeMillis(); + + while (!webServer.isRunning()) { + Thread.sleep(100); + if ((System.currentTimeMillis() - now) > timeout) { + Assertions.fail("Failed to start webserver"); + } + } + + webClient = WebClient.builder() + .baseUri("http://localhost:" + webServer.port()) + .addMediaSupport(JsonpSupport.create()) + .build(); + } + + @AfterAll + private static void stopServer() throws Exception { + if (webServer != null) { + webServer.shutdown() + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); + } + embeddedDatabaseServer.close(); + } + + @Test + void testMovies() throws Exception { + + webClient.get() + .path("api/movies") + .request(JsonArray.class) + .thenAccept(result -> Assertions.assertEquals("The Matrix", result.getJsonObject(0).getString("title"))) + .toCompletableFuture() + .get(); + + } + + @Test + public void testHealth() throws Exception { + + webClient.get() + .path("/health") + .request() + .thenAccept(response -> Assertions.assertEquals(200, response.status().code())) + .toCompletableFuture() + .get(); + } + + @Test + public void testMetrics() throws Exception { + webClient.get() + .path("/metrics") + .request() + .thenAccept(response -> Assertions.assertEquals(200, response.status().code())) + .toCompletableFuture() + .get(); + } + + static final String FIXTURE = "" + + "CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'})\n" + + "CREATE (Keanu:Person {name:'Keanu Reeves', born:1964})\n" + + "CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967})\n" + + "CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961})\n" + + "CREATE (Hugo:Person {name:'Hugo Weaving', born:1960})\n" + + "CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967})\n" + + "CREATE (LanaW:Person {name:'Lana Wachowski', born:1965})\n" + + "CREATE (JoelS:Person {name:'Joel Silver', born:1952})\n" + + "CREATE (KevinB:Person {name:'Kevin Bacon', born:1958})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrix),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrix),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrix)\n" + + "\n" + + "CREATE (Emil:Person {name:\"Emil Eifrem\", born:1978})\n" + + "CREATE (Emil)-[:ACTED_IN {roles:[\"Emil\"]}]->(TheMatrix)\n" + + "\n" + + "CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrixReloaded),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrixReloaded),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrixReloaded)\n" + + "\n" + + "CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'})\n" + + "CREATE\n" + + "(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions),\n" + + "(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions),\n" + + "(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions),\n" + + "(KevinB)-[:ACTED_IN {roles:['Unknown']}]->(TheMatrixRevolutions),\n" + + "(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions),\n" + + "(LillyW)-[:DIRECTED]->(TheMatrixRevolutions),\n" + + "(LanaW)-[:DIRECTED]->(TheMatrixRevolutions),\n" + + "(JoelS)-[:PRODUCED]->(TheMatrixRevolutions)\n"; +} \ No newline at end of file diff --git a/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/package-info.java b/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/package-info.java new file mode 100644 index 00000000000..55429240b5e --- /dev/null +++ b/examples/integrations/neo4j/neo4j-se/src/test/java/io/helidon/examples/quickstart/se/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Tests for Neo4j Helidon SE app. + */ +package io.helidon.examples.quickstart.se; diff --git a/examples/integrations/neo4j/pom.xml b/examples/integrations/neo4j/pom.xml new file mode 100644 index 00000000000..a0b8a1d9620 --- /dev/null +++ b/examples/integrations/neo4j/pom.xml @@ -0,0 +1,38 @@ + + + + + 4.0.0 + + io.helidon.examples.integrations + helidon-examples-integrations-project + 2.2.1-SNAPSHOT + + io.helidon.examples.integrations.neo4j + helidon-examples-integrations-neo4j-project + Helidon Neo4j Integrations Examples + pom + + + neo4j-mp + neo4j-se + + + diff --git a/examples/integrations/pom.xml b/examples/integrations/pom.xml index 896dcc77a26..2f3717efe3b 100644 --- a/examples/integrations/pom.xml +++ b/examples/integrations/pom.xml @@ -33,6 +33,7 @@ cdi micronaut + neo4j diff --git a/integrations/neo4j/health/pom.xml b/integrations/neo4j/health/pom.xml new file mode 100644 index 00000000000..20e467b15dc --- /dev/null +++ b/integrations/neo4j/health/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-project + 2.2.1-SNAPSHOT + + + helidon-integrations-neo4j-health + Helidon Neo4j Health Integrations + + + + org.neo4j.driver + neo4j-java-driver + provided + + + org.eclipse.microprofile.health + microprofile-health-api + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + true + + + org.junit.jupiter + junit-jupiter-api + test + + + + diff --git a/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/Neo4jHealthCheck.java b/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/Neo4jHealthCheck.java new file mode 100644 index 00000000000..2d89d4aa469 --- /dev/null +++ b/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/Neo4jHealthCheck.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j.health; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Readiness; +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Result; +import org.neo4j.driver.Session; +import org.neo4j.driver.SessionConfig; +import org.neo4j.driver.exceptions.SessionExpiredException; +import org.neo4j.driver.summary.ResultSummary; +import org.neo4j.driver.summary.ServerInfo; + +/** + * Health support module for Neo4j. Follows the standard MicroProfile HealthCheck pattern. + */ +@Readiness +@ApplicationScoped +public class Neo4jHealthCheck implements HealthCheck { + + /** + * The Cypher statement used to verify Neo4j is up. + */ + private static final String CYPHER = "RETURN 1 AS result"; + + private static final SessionConfig DEFAULT_SESSION_CONFIG = SessionConfig.builder() + .withDefaultAccessMode(AccessMode.WRITE) + .build(); + + private final Driver driver; + + @Inject + //will be ignored outside of CDI + Neo4jHealthCheck(Driver driver) { + this.driver = driver; + } + + /** + * To be used in SE context. + * + * @param driver create + * @return Driver + */ + public static Neo4jHealthCheck create(Driver driver) { + return new Neo4jHealthCheck(driver); + } + + /** + * Applies the given ResultSummaryto the HealthCheckResponseBuilder builder and calls build + * afterwards. + * + * @param resultSummary the result summary returned by the server + * @param builder the health builder to be modified + * @return the final HealthCheckResponse health check response + */ + private static HealthCheckResponse buildStatusUp(ResultSummary resultSummary, HealthCheckResponseBuilder builder) { + ServerInfo serverInfo = resultSummary.server(); + + builder.withData("server", serverInfo.version() + "@" + serverInfo.address()); + + String databaseName = resultSummary.database().name(); + if (!(databaseName == null || databaseName.trim().isEmpty())) { + builder.withData("database", databaseName.trim()); + } + + return builder.build(); + } + + @Override + public HealthCheckResponse call() { + + HealthCheckResponseBuilder builder = HealthCheckResponse.named("Neo4j connection health check").up(); + try { + ResultSummary resultSummary; + // Retry one time when the session has been expired + try { + resultSummary = runHealthCheckQuery(); + } catch (SessionExpiredException sessionExpiredException) { + resultSummary = runHealthCheckQuery(); + } + return buildStatusUp(resultSummary, builder); + } catch (Exception e) { + return builder.down().withData("reason", e.getMessage()).build(); + } + } + + private ResultSummary runHealthCheckQuery() { + // We use WRITE here to make sure UP is returned for a server that supports + // all possible workloads + if (driver != null) { + Session session = this.driver.session(DEFAULT_SESSION_CONFIG); + + Result run = session.run(CYPHER); + return run.consume(); + } + return null; + } +} diff --git a/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/package-info.java b/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/package-info.java new file mode 100644 index 00000000000..eb82d0d2aa5 --- /dev/null +++ b/integrations/neo4j/health/src/main/java/io/helidon/integrations/neo4j/health/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Health check for Neo4j. + */ +package io.helidon.integrations.neo4j.health; diff --git a/integrations/neo4j/health/src/main/java/module-info.java b/integrations/neo4j/health/src/main/java/module-info.java new file mode 100644 index 00000000000..1a5a6e0430e --- /dev/null +++ b/integrations/neo4j/health/src/main/java/module-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Neo4j health checks module. + */ +module io.helidon.integrations.neo4j.health { + requires microprofile.health.api; + requires org.neo4j.driver; + + requires static jakarta.enterprise.cdi.api; + requires static jakarta.inject.api; + + exports io.helidon.integrations.neo4j.health; + + opens io.helidon.integrations.neo4j.health to weld.core.impl, io.helidon.microprofile.cdi; +} diff --git a/integrations/neo4j/health/src/main/resources/META-INF/beans.xml b/integrations/neo4j/health/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..1f940258445 --- /dev/null +++ b/integrations/neo4j/health/src/main/resources/META-INF/beans.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/integrations/neo4j/helidon-neo4j-proposal.md b/integrations/neo4j/helidon-neo4j-proposal.md new file mode 100644 index 00000000000..e1f9c51fe0f --- /dev/null +++ b/integrations/neo4j/helidon-neo4j-proposal.md @@ -0,0 +1,62 @@ +# Helidon Neo4j support proposal + +## Proposal + +Currently, there is no proper integration between Neo4j and Helidon. +Neo4j is a de facto standard for graph databases worldwide. Supporting it out of the box will be a huge benefit for Helidon. + +## From the Helidon side: + +For Helidon MP and SE there are three projects: +* Support: Contains SE integration with the main idea to expose a configured driver. There is also the CDI extension that delegates all the initialization and configuration to SE support. +* Health: health checks for the Neo4j based on the driver. Provided as a separate module. +* Metrics: metrics exposition. Provides a wrapper over Neo4j metrics and exposes them for SE. They are available as MP Metrics as well. Provided as a separate module. + +### Usage for SE +```java +Neo4JSupport neo4j = Neo4JSupport.builder() + .config(config) + .helper(Neo4JMetricsSupport.create()) //optional support for Neo4j Metrics + .helper(Neo4JHealthSupport.create()) //optional support for Neo4j Health checks + .build(); + + Routing.builder() + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics" + .register(movieService) + .build(); +``` + +### Usage for MP +Just inject the driver: +```java +@Inject +Driver driver; +``` + +### Configuration + +May be done in application.yaml: + +```yaml +neo4j: + uri: bolt://localhost:7687 + authentication: + username: neo4j + password: secret + pool: + metricsEnabled: true #should be explicitly enabled in Neo4j driver +``` +or via MicroProfile Config: + +```properties +neo4j.uri=bolt://localhost:7687 +neo4j.authentication.username=neo4j +neo4j.authentication.password: secret +neo4j.pool.metricsEnabled: true #should be explicitly enabled in Neo4j driver + +``` + + +At this point there is no need to create or use any kind of Object Mapping. Exposing the driver, providing health checks +and metrics should be considered as enough. \ No newline at end of file diff --git a/integrations/neo4j/metrics/pom.xml b/integrations/neo4j/metrics/pom.xml new file mode 100644 index 00000000000..0757e8ad84e --- /dev/null +++ b/integrations/neo4j/metrics/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-project + 2.2.1-SNAPSHOT + + + helidon-integrations-neo4j-metrics + Helidon Neo4j Metrics Integrations + + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j + + + org.neo4j.driver + neo4j-java-driver + provided + + + io.helidon.metrics + helidon-metrics + + + org.eclipse.microprofile.metrics + microprofile-metrics-api + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + true + + + + diff --git a/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsCdiExtension.java b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsCdiExtension.java new file mode 100644 index 00000000000..6b3118ea0e0 --- /dev/null +++ b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsCdiExtension.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j.metrics; + +import javax.annotation.Priority; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Initialized; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.CDI; +import javax.enterprise.inject.spi.Extension; + +import org.neo4j.driver.Driver; + +import static javax.interceptor.Interceptor.Priority.PLATFORM_AFTER; + +/** + * CDI Extension, instantiated by CDI. + */ +public class Neo4jMetricsCdiExtension implements Extension { + + private void addMetrics(@Observes @Priority(PLATFORM_AFTER + 101) @Initialized(ApplicationScoped.class) Object event) { + Instance driver = CDI.current().select(Driver.class); + Neo4jMetricsSupport.builder() + .driver(driver.get()) + .build() + .initialize(); + } +} diff --git a/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsSupport.java b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsSupport.java new file mode 100644 index 00000000000..792960e3b07 --- /dev/null +++ b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/Neo4jMetricsSupport.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j.metrics; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.function.Supplier; + +import io.helidon.common.LazyValue; +import io.helidon.metrics.RegistryFactory; + +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.Gauge; +import org.eclipse.microprofile.metrics.Metadata; +import org.eclipse.microprofile.metrics.MetricID; +import org.eclipse.microprofile.metrics.MetricRegistry; +import org.eclipse.microprofile.metrics.MetricType; +import org.neo4j.driver.ConnectionPoolMetrics; +import org.neo4j.driver.Driver; + +import static java.util.Map.entry; + +/** + * Neo4j helper class to support metrics. Provided as a separate package to be included as a dependency. + */ +public class Neo4jMetricsSupport { + + private static final String NEO4J_METRIC_NAME_PREFIX = "neo4j"; + + private final AtomicReference> lastPoolMetrics = new AtomicReference<>(); + private final AtomicReference> reinitFuture = new AtomicReference<>(); + private final AtomicReference metricsInitialized = new AtomicReference<>(false); + private final LazyValue metricRegistry; + private final Driver driver; + + private Neo4jMetricsSupport(Builder builder) { + this.driver = builder.driver; + // Assuming for the moment that VENDOR is the correct registry to use. + metricRegistry = LazyValue.create(() -> RegistryFactory.getInstance().getRegistry(MetricRegistry.Type.VENDOR)); + } + + /** + * Following the builder pattern. + * + * @return builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Register metrics and start monitoring. + */ + public void initialize() { + this.lastPoolMetrics.set(driver.metrics().connectionPoolMetrics()); + + reinit(); + + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "neo4j-metrics-init")); + reinitFuture.set(executor.scheduleAtFixedRate(() -> refreshMetrics(executor), 10, 10, TimeUnit.SECONDS)); + } + + private void refreshMetrics(ScheduledExecutorService executor) { + Collection currentPoolMetrics = driver.metrics().connectionPoolMetrics(); + + if (!metricsInitialized.get() && currentPoolMetrics.size() >= 1) { + lastPoolMetrics.set(currentPoolMetrics); + reinit(); + + if (metricsInitialized.get()) { + reinitFuture.get().cancel(false); + executor.shutdown(); + } + } + } + + private void reinit() { + Map> counters = Map.ofEntries( + entry("acquired", ConnectionPoolMetrics::acquired), + entry("closed", ConnectionPoolMetrics::closed), + entry("created", ConnectionPoolMetrics::created), + entry("failedToCreate", ConnectionPoolMetrics::failedToCreate), + entry("timedOutToAcquire", ConnectionPoolMetrics::timedOutToAcquire), + entry("totalAcquisitionTime", ConnectionPoolMetrics::totalAcquisitionTime), + entry("totalConnectionTime", ConnectionPoolMetrics::totalConnectionTime), + entry("totalInUseCount", ConnectionPoolMetrics::totalInUseCount), + entry("totalInUseTime", ConnectionPoolMetrics::totalInUseTime)); + + Map> gauges = Map.ofEntries( + entry("acquiring", ConnectionPoolMetrics::acquiring), + entry("creating", ConnectionPoolMetrics::creating), + entry("idle", ConnectionPoolMetrics::idle), + entry("inUse", ConnectionPoolMetrics::inUse) + ); + for (ConnectionPoolMetrics it : lastPoolMetrics.get()) { + //String poolPrefix = NEO4J_METRIC_NAME_PREFIX + "-" + it.id() + "-"; + String poolPrefix = NEO4J_METRIC_NAME_PREFIX + "-"; + counters.forEach((name, supplier) -> registerCounter(metricRegistry.get(), it, poolPrefix, name, supplier)); + gauges.forEach((name, supplier) -> registerGauge(metricRegistry.get(), it, poolPrefix, name, supplier)); + // we only care about the first one + metricsInitialized.set(true); + break; + } + } + + private void registerCounter(MetricRegistry metricRegistry, + ConnectionPoolMetrics cpm, + String poolPrefix, + String name, + Function fn) { + + String counterName = poolPrefix + name; + if (metricRegistry.getCounters().get(new MetricID(counterName)) == null) { + Metadata metadata = Metadata.builder() + .withName(counterName) + .withType(MetricType.COUNTER) + .notReusable() + .build(); + Neo4JCounterWrapper wrapper = new Neo4JCounterWrapper(() -> fn.apply(cpm)); + metricRegistry.register(metadata, wrapper); + } + } + + private void registerGauge(MetricRegistry metricRegistry, + ConnectionPoolMetrics cpm, + String poolPrefix, + String name, + Function fn) { + + String gaugeName = poolPrefix + name; + if (metricRegistry.getGauges().get(new MetricID(gaugeName)) == null) { + Metadata metadata = Metadata.builder() + .withName(poolPrefix + name) + .withType(MetricType.GAUGE) + .notReusable() + .build(); + Neo4JGaugeWrapper wrapper = + new Neo4JGaugeWrapper<>(() -> fn.apply(cpm)); + metricRegistry.register(metadata, wrapper); + } + } + + /** + * Fluent API builder for Neo4jMetricsSupport. + */ + public static class Builder implements io.helidon.common.Builder { + + private Driver driver; + + private Builder() { + } + + /** + * Builder for the wrapper class. + * + * @return wrapper + */ + public Neo4jMetricsSupport build() { + Objects.requireNonNull(driver, "Must set driver before building"); + return new Neo4jMetricsSupport(this); + } + + /** + * Submit the Neo4j driver. + * + * @param driver from the support class + * @return Builder + */ + public Builder driver(Driver driver) { + this.driver = driver; + return this; + } + } + + private static class Neo4JCounterWrapper implements Counter { + + private final Supplier fn; + + private Neo4JCounterWrapper(Supplier fn) { + this.fn = fn; + } + + @Override + public void inc() { + throw new UnsupportedOperationException(); + } + + @Override + public void inc(long n) { + throw new UnsupportedOperationException(); + } + + @Override + public long getCount() { + return fn.get(); + } + } + + private static class Neo4JGaugeWrapper implements Gauge { + + private final Supplier supplier; + + private Neo4JGaugeWrapper(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T getValue() { + return supplier.get(); + } + } +} diff --git a/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/package-info.java b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/package-info.java new file mode 100644 index 00000000000..6d931d5f3a2 --- /dev/null +++ b/integrations/neo4j/metrics/src/main/java/io/helidon/integrations/neo4j/metrics/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Metrics package. + */ +package io.helidon.integrations.neo4j.metrics; diff --git a/integrations/neo4j/metrics/src/main/java/module-info.java b/integrations/neo4j/metrics/src/main/java/module-info.java new file mode 100644 index 00000000000..692f71c5065 --- /dev/null +++ b/integrations/neo4j/metrics/src/main/java/module-info.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Neo4j metrics support module. + */ +module io.helidon.integrations.neo4j.metrics { + + requires io.helidon.common; + requires io.helidon.integrations.neo4j; + + requires org.neo4j.driver; + + requires microprofile.metrics.api; + requires io.helidon.metrics; + + requires static jakarta.enterprise.cdi.api; + requires static jakarta.inject.api; + requires static jakarta.interceptor.api; + requires static java.annotation; + + exports io.helidon.integrations.neo4j.metrics; + + provides javax.enterprise.inject.spi.Extension with io.helidon.integrations.neo4j.metrics.Neo4jMetricsCdiExtension; + +} diff --git a/integrations/neo4j/metrics/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/integrations/neo4j/metrics/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000000..a1c3225a302 --- /dev/null +++ b/integrations/neo4j/metrics/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +io.helidon.integrations.neo4j.metrics.Neo4jMetricsCdiExtension \ No newline at end of file diff --git a/integrations/neo4j/neo4j/pom.xml b/integrations/neo4j/neo4j/pom.xml new file mode 100644 index 00000000000..f5a461ce664 --- /dev/null +++ b/integrations/neo4j/neo4j/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-project + 2.2.1-SNAPSHOT + + + helidon-integrations-neo4j + Helidon Integrations Neo4j + + + + org.neo4j.driver + neo4j-java-driver + + + io.helidon.common + helidon-common + + + io.helidon.config + helidon-config + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + true + + + io.helidon.config + helidon-config-mp + provided + true + + + org.junit.jupiter + junit-jupiter-api + test + + + + diff --git a/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4j.java b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4j.java new file mode 100644 index 00000000000..a0911ce1609 --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4j.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.Objects; +import java.util.logging.Level; + +import io.helidon.config.Config; + +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Logging; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +/** + * Main entry point for Neo4j support for Helidon. + * Performs configuration and the prepared driver. + * + */ +public final class Neo4j { + private final Driver driver; + + private Neo4j(Builder builder) { + this.driver = builder.driver; + } + + /** + * Create the Neo4j support using builder. + * + * @param config from the external configuration + * @return Neo4j support + */ + public static Neo4j create(Config config) { + return builder().config(config).build(); + } + + /** + * Following the builder pattern. + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * The main entry point to the Neo4j Support. + * + * @return neo4j driver + */ + public Driver driver() { + return driver; + } + + /** + * Fluent API builder for Neo4j. + */ + public static final class Builder implements io.helidon.common.Builder { + private boolean encrypted; + private boolean authenticationEnabled = true; + private String username; + private String password; + private String uri; + + //pool + private boolean metricsEnabled; + private boolean logLeakedSessions; + private int maxConnectionPoolSize = 100; + private Duration idleTimeBeforeConnectionTest = Duration.ofMillis(-1); + private Duration maxConnectionLifetime = Duration.ofHours(1); + private Duration connectionAcquisitionTimeout = Duration.ofMinutes(1); + + //trust + private TrustStrategy trustStrategy = TrustStrategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES; + private Path certFile; + private boolean hostnameVerificationEnabled; + + // explicit driver + private Driver driver; + + private Builder() { + } + + /** + * The main build method. + * + * @return Neo4j + */ + @Override + public Neo4j build() { + if (driver == null) { + driver = initDriver(); + } + return new Neo4j(this); + } + + /** + * Read the configuration from external file and initialize the builder. + * @param config external configuration + * @return the builder + */ + public Builder config(Config config) { + config.get("authentication.username").asString().ifPresent(this::username); + config.get("authentication.password").asString().ifPresent(this::password); + config.get("authentication.enabled").asBoolean().ifPresent(this::authenticationEnabled); + config.get("uri").asString().ifPresent(this::uri); + config.get("encrypted").asBoolean().ifPresent(this::encrypted); + + //pool + config.get("pool.metricsEnabled").asBoolean().ifPresent(this::metricsEnabled); + config.get("pool.logLeakedSessions").asBoolean().ifPresent(this::logLeakedSessions); + config.get("pool.maxConnectionPoolSize").asInt().ifPresent(this::maxConnectionPoolSize); + config.get("pool.idleTimeBeforeConnectionTest").as(Duration.class).ifPresent(this::idleTimeBeforeConnectionTest); + config.get("pool.maxConnectionLifetime").as(Duration.class).ifPresent(this::maxConnectionLifetime); + config.get("pool.connectionAcquisitionTimeout").as(Duration.class).ifPresent(this::connectionAcquisitionTimeout); + + //trust + config.get("trustsettings.trustStrategy").asString().map(TrustStrategy::valueOf).ifPresent(this::trustStrategy); + config.get("trustsettings.certificate").as(Path.class).ifPresent(this::certificate); + config.get("trustsettings.hostnameVerificationEnabled").asBoolean().ifPresent(this::hostnameVerificationEnabled); + + return this; + } + + /** + * Create username. + * + * @param username parameter + * @return Builder + */ + public Builder username(String username) { + Objects.requireNonNull(username); + this.username = username; + this.authenticationEnabled = true; + return this; + + } + + /** + * Create password. + * + * @param password parameter + * @return Builder + */ + public Builder password(String password) { + Objects.requireNonNull(password); + this.password = password; + return this; + } + + /** + * Create uri. + * + * @param uri parameter + * @return Builder + */ + public Builder uri(String uri) { + Objects.requireNonNull(uri); + this.uri = uri; + return this; + } + + /** + * Enable ecrypted field. + * + * @param encrypted parameter + * @return Builder + */ + public Builder encrypted(boolean encrypted) { + this.encrypted = encrypted; + return this; + } + + /** + * Enable authentication. + * + * @param authenticationEnabled parameter + * @return Builder + */ + public Builder authenticationEnabled(boolean authenticationEnabled) { + this.authenticationEnabled = authenticationEnabled; + return this; + } + + /** + * Enagle metrics. + * + * @param metricsEnabled parameter + * @return Builder + */ + public Builder metricsEnabled(boolean metricsEnabled) { + this.metricsEnabled = metricsEnabled; + return this; + } + + /** + * Eable log leaked sessions. + * + * @param logLeakedSessions parameter + * @return Builder + */ + public Builder logLeakedSessions(boolean logLeakedSessions) { + this.logLeakedSessions = logLeakedSessions; + return this; + } + + /** + * Set pool size. + * + * @param maxConnectionPoolSize parameter + * @return Builder + */ + public Builder maxConnectionPoolSize(int maxConnectionPoolSize) { + this.maxConnectionPoolSize = maxConnectionPoolSize; + return this; + } + + /** + * Set idle time. + * + * @param idleTimeBeforeConnectionTest parameter + * @return Builder + */ + public Builder idleTimeBeforeConnectionTest(Duration idleTimeBeforeConnectionTest) { + Objects.requireNonNull(idleTimeBeforeConnectionTest); + this.idleTimeBeforeConnectionTest = idleTimeBeforeConnectionTest; + return this; + } + + /** + * Set max life time. + * + * @param maxConnectionLifetime parameter + * @return Builder + */ + public Builder maxConnectionLifetime(Duration maxConnectionLifetime) { + Objects.requireNonNull(maxConnectionLifetime); + this.maxConnectionLifetime = maxConnectionLifetime; + return this; + } + + /** + * Set connection acquisition timeout. + * + * @param connectionAcquisitionTimeout parameter + * @return Builder + */ + public Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { + Objects.requireNonNull(connectionAcquisitionTimeout); + this.connectionAcquisitionTimeout = connectionAcquisitionTimeout; + return this; + } + + /** + * Set trust strategy. + * + * @param strategy parameter + * @return Builder + */ + public Builder trustStrategy(TrustStrategy strategy) { + this.trustStrategy = strategy; + return this; + } + + /** + * Set certificate path. + * + * @param certFile parameter + * @return Builder + */ + public Builder certificate(Path certFile) { + this.certFile = certFile; + return this; + } + + /** + * Enable hostname verification. + * + * @param hostnameVerificationEnabled parameter + * @return Builder + */ + public Builder hostnameVerificationEnabled(boolean hostnameVerificationEnabled) { + this.hostnameVerificationEnabled = hostnameVerificationEnabled; + return this; + } + + /** + * Neo4j base driver construction method. + * + * @return the driver + */ + private Driver initDriver() { + AuthToken authToken = AuthTokens.none(); + if (authenticationEnabled) { + authToken = AuthTokens.basic(username, password); + } + + org.neo4j.driver.Config.ConfigBuilder configBuilder = createBaseConfig(); + configureSsl(configBuilder); + configurePoolSettings(configBuilder); + + return GraphDatabase.driver(uri, authToken, configBuilder.build()); + + } + + private void configureSsl(org.neo4j.driver.Config.ConfigBuilder configBuilder) { + + if (encrypted) { + configBuilder.withEncryption(); + configBuilder.withTrustStrategy(toInternalTrustStrategy()); + } else { + configBuilder.withoutEncryption(); + } + } + + private org.neo4j.driver.Config.TrustStrategy toInternalTrustStrategy() { + org.neo4j.driver.Config.TrustStrategy internalRepresentation; + switch (trustStrategy) { + case TRUST_ALL_CERTIFICATES: + internalRepresentation = org.neo4j.driver.Config.TrustStrategy.trustAllCertificates(); + break; + case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES: + internalRepresentation = org.neo4j.driver.Config.TrustStrategy.trustSystemCertificates(); + break; + case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES: + if (certFile == null) { + throw new Neo4jException("Configured trust trustStrategy " + trustStrategy.name() + + " requires a certificate file, " + + "configured through builder, or using trustsettings.certificate " + + "configuration property."); + } + if (Files.isRegularFile(certFile)) { + internalRepresentation = org.neo4j.driver.Config.TrustStrategy + .trustCustomCertificateSignedBy(certFile.toFile()); + } else { + throw new Neo4jException("Configured trust trustStrategy requires a certificate file, but got: " + certFile + .toAbsolutePath()); + } + break; + default: + throw new Neo4jException("Unknown trust trustStrategy: " + this.trustStrategy.name()); + } + + if (hostnameVerificationEnabled) { + internalRepresentation.withHostnameVerification(); + } else { + internalRepresentation.withoutHostnameVerification(); + } + return internalRepresentation; + } + + private void configurePoolSettings(org.neo4j.driver.Config.ConfigBuilder configBuilder) { + + if (logLeakedSessions) { + configBuilder.withLeakedSessionsLogging(); + } + configBuilder.withMaxConnectionPoolSize(maxConnectionPoolSize); + configBuilder.withConnectionLivenessCheckTimeout(idleTimeBeforeConnectionTest.toMillis(), MILLISECONDS); + configBuilder.withMaxConnectionLifetime(maxConnectionLifetime.toMillis(), MILLISECONDS); + configBuilder.withConnectionAcquisitionTimeout(connectionAcquisitionTimeout.toMillis(), MILLISECONDS); + + if (metricsEnabled) { + configBuilder.withDriverMetrics(); + } else { + configBuilder.withoutDriverMetrics(); + } + } + + /** + * Neo4j base config helper method. + * + * @return ConfigBuilder + */ + private static org.neo4j.driver.Config.ConfigBuilder createBaseConfig() { + org.neo4j.driver.Config.ConfigBuilder configBuilder = org.neo4j.driver.Config.builder(); + Logging logging; + try { + logging = Logging.slf4j(); + } catch (Exception e) { + logging = Logging.javaUtilLogging(Level.INFO); + } + configBuilder.withLogging(logging); + return configBuilder; + } + + /** + * Security trustStrategy. + */ + public enum TrustStrategy { + /** + * Trust all. + */ + TRUST_ALL_CERTIFICATES, + /** + * Trust custom certificates. + */ + TRUST_CUSTOM_CA_SIGNED_CERTIFICATES, + /** + * Trust system CA. + */ + TRUST_SYSTEM_CA_SIGNED_CERTIFICATES + } + + } +} diff --git a/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jCdiExtension.java b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jCdiExtension.java new file mode 100644 index 00000000000..69cd2978f2e --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jCdiExtension.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Extension; + +import io.helidon.config.Config; +import io.helidon.config.ConfigValue; +import io.helidon.config.mp.MpConfig; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.neo4j.driver.Driver; + +/** + * A CDI Extension for Neo4j support. To be used in MP environment. Delegates all of it activities to + * Neo4j for initialization and configuration. + * + */ +public class Neo4jCdiExtension implements Extension { + + private static final String NEO4J_CONFIG_NAME_PREFIX = "neo4j"; + + void afterBeanDiscovery(@Observes AfterBeanDiscovery addEvent) { + addEvent.addBean() + .types(Driver.class) + .qualifiers(Default.Literal.INSTANCE, Any.Literal.INSTANCE) + .scope(ApplicationScoped.class) + .name(Driver.class.getName()) + .beanClass(Driver.class) + .createWith(creationContext -> { + org.eclipse.microprofile.config.Config config = ConfigProvider.getConfig(); + Config helidonConfig = MpConfig.toHelidonConfig(config).get(NEO4J_CONFIG_NAME_PREFIX); + + ConfigValue configValue = helidonConfig.as(Neo4j::create); + if (configValue.isPresent()) { + return configValue.get().driver(); + } + throw new Neo4jException("There is no Neo4j driver configured in configuration under key 'neo4j"); + }); + } +} diff --git a/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jException.java b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jException.java new file mode 100644 index 00000000000..6e752c7b1cc --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/Neo4jException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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.helidon.integrations.neo4j; + +/** + * Helidon exception marking a problem with Neo4j integration setup or runtime. + */ +public class Neo4jException extends RuntimeException { + /** + * Neo4jException constructor with message. + * + * @param message parameter + */ + public Neo4jException(String message) { + super(message); + } + + /** + * Neo4jException constructor with message and throwable. + * + * @param message parameter + * @param cause parameter + */ + public Neo4jException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/package-info.java b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/package-info.java new file mode 100644 index 00000000000..af3cb77f305 --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/java/io/helidon/integrations/neo4j/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ +/** + * Neo4j integrations for Helidon. + */ +package io.helidon.integrations.neo4j; diff --git a/integrations/neo4j/neo4j/src/main/java/module-info.java b/integrations/neo4j/neo4j/src/main/java/module-info.java new file mode 100644 index 00000000000..7e2f6779bd9 --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/java/module-info.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * 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. + */ + +/** + * Neo4j support module. + */ +module io.helidon.integrations.neo4j { + requires java.logging; + + requires static jakarta.enterprise.cdi.api; + requires static jakarta.inject.api; + requires static jakarta.interceptor.api; + requires static io.helidon.config; + requires static io.helidon.config.mp; + + requires org.neo4j.driver; + + exports io.helidon.integrations.neo4j; + + provides javax.enterprise.inject.spi.Extension with io.helidon.integrations.neo4j.Neo4jCdiExtension; +} diff --git a/integrations/neo4j/neo4j/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/integrations/neo4j/neo4j/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000000..eafc9021d0e --- /dev/null +++ b/integrations/neo4j/neo4j/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. +# +# 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. +# + +io.helidon.integrations.neo4j.Neo4jCdiExtension \ No newline at end of file diff --git a/integrations/neo4j/pom.xml b/integrations/neo4j/pom.xml new file mode 100644 index 00000000000..5fe963083ba --- /dev/null +++ b/integrations/neo4j/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + io.helidon.integrations + helidon-integrations-project + 2.2.1-SNAPSHOT + + io.helidon.integrations.neo4j + helidon-integrations-neo4j-project + pom + Helidon Neo4j Integrations + + + neo4j + health + metrics + + diff --git a/integrations/pom.xml b/integrations/pom.xml index eda04a332d8..2a93d812854 100644 --- a/integrations/pom.xml +++ b/integrations/pom.xml @@ -44,6 +44,7 @@ graal db micronaut + neo4j From edc2a7c289add584d2b8bb2cb1efd911cff1068f Mon Sep 17 00:00:00 2001 From: dalexandrov Date: Tue, 26 Jan 2021 17:19:43 +0200 Subject: [PATCH 2/3] Copyright year fixed. --- bom/pom.xml | 2 +- .../common/src/main/java/io/helidon/common/FeatureCatalog.java | 2 +- .../io/helidon/examples/integrations/neo4j/mp/domain/Actor.java | 2 +- .../io/helidon/examples/integrations/neo4j/mp/domain/Movie.java | 2 +- .../examples/integrations/neo4j/mp/domain/MovieRepository.java | 2 +- .../helidon/examples/integrations/neo4j/mp/domain/Person.java | 2 +- .../examples/integrations/neo4j/mp/domain/package-info.java | 2 +- .../io/helidon/examples/integrations/neo4j/se/domain/Actor.java | 2 +- .../io/helidon/examples/integrations/neo4j/se/domain/Movie.java | 2 +- .../examples/integrations/neo4j/se/domain/MovieRepository.java | 2 +- .../helidon/examples/integrations/neo4j/se/domain/Person.java | 2 +- .../examples/integrations/neo4j/se/domain/package-info.java | 2 +- examples/integrations/pom.xml | 2 +- integrations/pom.xml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index bd51cf7bdb7..364463e3e20 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -1,7 +1,7 @@