From 5a9916c241713770596cadec3b0ef55f8c586605 Mon Sep 17 00:00:00 2001 From: sbespalov Date: Mon, 27 Jan 2020 16:59:51 +0700 Subject: [PATCH 1/5] issues/1650 Migrate Strongbox from OrientDB to JanusGraph --- Jenkinsfile | 2 +- README.md | 8 +- pom.xml | 6 +- strongbox-db-import/pom.xml | 60 +- .../src/main/assembly/db-schema.xml | 4 +- .../carlspring/strongbox/db/Application.java | 3 +- .../db/orient/OrientDbConfiguration.java | 106 --- .../strongbox/db/schema/SchemaConfig.java | 16 + .../server/EmbeddedDbServerConfiguration.java | 53 ++ .../src/main/resources/application.yml | 24 +- .../db/changelog/db.changelog-master.xml | 9 - .../changelog/v1.0.0/db.changelog-1.0.0.xml | 30 - .../v1.0.0/v1.0.0.10__ArtifactEntry.xml | 13 - .../v1.0.0.11__ArtifactEntry_cleanup.xml | 13 - .../v1.0.0.12__Configuration_cleanup.xml | 75 --- .../v1.0.0/v1.0.0.13__ArtifactEntry_path.xml | 15 - .../v1.0.0/v1.0.0.14__User_cleanup.xml | 21 - .../v1.0.0.15__Authorization_cleanup.xml | 19 - ....0.0.16__CronTaskConfiguration_cleanup.xml | 15 - .../v1.0.0.17__ArtifactArchiveListing.xml | 13 - .../v1.0.0/v1.0.0.18__ArtifactGroup.xml | 23 - .../v1.0.0/v1.0.0.19__ArtifactEntry.xml | 14 - .../v1.0.0/v1.0.0.1__GenericEntity.xml | 19 - .../db/changelog/v1.0.0/v1.0.0.20__User.xml | 24 - .../v1.0.0/v1.0.0.21__ArtifactEntry.xml | 38 -- .../changelog/v1.0.0/v1.0.0.22__UserEntry.xml | 15 - .../v1.0.0/v1.0.0.2__ArtifactEntry.xml | 42 -- .../v1.0.0/v1.0.0.3__AuthorizationConfig.xml | 25 - .../v1.0.0/v1.0.0.4__BinaryConfiguration.xml | 15 - .../v1.0.0.5__CronTaskConfiguration.xml | 18 - .../db/changelog/v1.0.0/v1.0.0.6__User.xml | 28 - .../v1.0.0/v1.0.0.7__Configuration.xml | 133 ---- .../v1.0.0/v1.0.0.8__AuthorizationConfig.xml | 19 - .../changelog/v1.0.0/v1.0.0.9__Repository.xml | 19 - .../pom.xml | 8 +- .../carlspring/strongbox/db/schema/Edges.java | 12 + .../strongbox/db/schema/Properties.java | 48 ++ .../strongbox/db/schema/StrongboxSchema.java | 615 ++++++++++++++++++ .../strongbox/db/schema/Vertices.java | 19 + strongbox-db-server/pom.xml | 68 +- .../driver/CypherGremlinStatementRunner.java | 168 +++++ .../driver/Neo4jDriverEntityAdapter.java | 117 ++++ .../gremlin/neo4j/ogm/CypherQueryUtils.java | 294 +++++++++ .../gremlin/neo4j/ogm/GremlinGraphDriver.java | 79 +++ .../neo4j/ogm/request/GremlinRequest.java | 262 ++++++++ .../GremlinGraphRowModelResponse.java | 42 ++ .../ogm/response/GremlinModelResponse.java | 118 ++++ .../neo4j/ogm/response/GremlinResponse.java | 47 ++ .../response/GremlinRestModelResponse.java | 155 +++++ .../ogm/response/GremlinRowModelResponse.java | 65 ++ .../ogm/transaction/GremlinTransaction.java | 120 ++++ .../CassandraEmbeddedConfiguration.java | 11 + .../server/CassandraEmbeddedProperties.java | 122 ++++ .../strongbox/db/server/EmbeddedDbServer.java | 10 + ...EmbeddedJanusGraphWithCassandraServer.java | 220 +++++++ .../db/server/EmbeddedOrientDbServer.java | 283 -------- .../db/server/InMemoryJanusGraphServer.java | 131 ++++ .../db/server/JanusGraphConfiguration.java | 16 + .../db/server/JanusGraphProperties.java | 84 +++ .../strongbox/db/server/OrientDbServer.java | 10 - .../server/OrientDbServerConfiguration.java | 22 - .../db/server/OrientDbServerProperties.java | 114 ---- .../server/OrientDbStudioConfiguration.java | 14 - .../db/server/OrientDbStudioProperties.java | 67 -- .../main/java/org/strongbox/util/Commons.java | 49 ++ 65 files changed, 2956 insertions(+), 1371 deletions(-) delete mode 100644 strongbox-db-import/src/main/java/org/carlspring/strongbox/db/orient/OrientDbConfiguration.java create mode 100644 strongbox-db-import/src/main/java/org/carlspring/strongbox/db/schema/SchemaConfig.java create mode 100644 strongbox-db-import/src/main/java/org/carlspring/strongbox/db/server/EmbeddedDbServerConfiguration.java delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/db.changelog-master.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/db.changelog-1.0.0.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.10__ArtifactEntry.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.11__ArtifactEntry_cleanup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.12__Configuration_cleanup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.13__ArtifactEntry_path.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.14__User_cleanup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.15__Authorization_cleanup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.16__CronTaskConfiguration_cleanup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.17__ArtifactArchiveListing.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.18__ArtifactGroup.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.19__ArtifactEntry.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.1__GenericEntity.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.20__User.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.21__ArtifactEntry.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.22__UserEntry.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.2__ArtifactEntry.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.3__AuthorizationConfig.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.4__BinaryConfiguration.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.5__CronTaskConfiguration.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.6__User.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.7__Configuration.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.8__AuthorizationConfig.xml delete mode 100644 strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.9__Repository.xml rename {strongbox-db-liquibase => strongbox-db-schema}/pom.xml (75%) create mode 100644 strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java create mode 100644 strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java create mode 100644 strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java create mode 100644 strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/CypherGremlinStatementRunner.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/Neo4jDriverEntityAdapter.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/CypherQueryUtils.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/GremlinGraphDriver.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/request/GremlinRequest.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinGraphRowModelResponse.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinModelResponse.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinResponse.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRestModelResponse.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRowModelResponse.java create mode 100644 strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/transaction/GremlinTransaction.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedConfiguration.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedProperties.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedDbServer.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedJanusGraphWithCassandraServer.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedOrientDbServer.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/InMemoryJanusGraphServer.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphConfiguration.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphProperties.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServer.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerConfiguration.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerProperties.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioConfiguration.java delete mode 100644 strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioProperties.java create mode 100644 strongbox-db-server/src/main/java/org/strongbox/util/Commons.java diff --git a/Jenkinsfile b/Jenkinsfile index 26619af..c5a699b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,7 +12,7 @@ def notifyBranch = [recipients: [brokenTestsSuspects(), requestor()]] pipeline { agent { node { - label 'alpine-jdk8-mvn-3.5' + label 'alpine-jdk8-mvn3.6' } } parameters { diff --git a/README.md b/README.md index e996f8d..83f2a95 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # strongbox-db ## The goal of this project -This project has been created in order speed up the building of the OrientDB database snapshots during the initial [Strongbox](https://github.com/strongbox/strongbox) startup. Before this project was born, it took some time to do all of the database changes from the beginning of time up until the most recent version, so that the OrientDB database structure would be populated properly. +This project has been created in order speed up the building of the JanusGraph database snapshots during the initial [Strongbox](https://github.com/strongbox/strongbox) startup. Before this project was born, it took some time to do all of the database changes from the beginning of time up until the most recent version, so that the JanusGraph database structure would be populated properly. ## Architecture This application consists of the following modules: -* `strongbox-db-liquibase` which creates a `jar` file consiting of the liquibase changesets that construct the [Strongbox](https://github.com/strongbox/strongbox) OrientDB database schema +* `strongbox-db-liquibase` which creates a `jar` file consiting of the liquibase changesets that construct the [Strongbox](https://github.com/strongbox/strongbox) JanusGraph database schema * `strongbox-db-import` which creates a `zip` file with packed built database snapshot built from the liquibase changesets ## How is this project used in [strongbox](https://github.com/strongbox/strongbox) ? -[strongbox](https://github.com/strongbox/strongbox) uses this project submodules as required dependencies. During the startup of [Strongbox](https://github.com/strongbox/strongbox), the application detects if an OrientDB database already exists. If not, then the application uses an extracted empty OrientDB database snapshot from the `strongbox-db-import` artifact. If the OrientDB database already exists, then the application applies all missing liquibase changesets from the `strongbox-db-liquibase` artifact. +[strongbox](https://github.com/strongbox/strongbox) uses this project submodules as required dependencies. During the startup of [Strongbox](https://github.com/strongbox/strongbox), the application detects if an JanusGraph database already exists. If not, then the application uses an extracted empty JanusGraph database snapshot from the `strongbox-db-import` artifact. If the JanusGraph database already exists, then the application applies all missing changesets from the `strongbox-db-schema` artifact. ## How to build this project ? To build the code, simply execute: @@ -17,5 +17,5 @@ To build the code, simply execute: ## What's the result of the build process ? This project produces the following artifacts: -* `strongbox-db-liquibase-${version}.jar` : Located in `strongbox-db-liquibase/target`, which contains current database liquibase changesets +* `strongbox-db-shcema-${version}.jar` : Located in `strongbox-db-schema/target`, which contains current database changesets * `strongbox-db-import-${version}.zip` : Located in `strongbox-db-import/target`, which contains zipped fresh database snapshot built from the above changesets diff --git a/pom.xml b/pom.xml index 7cad30f..1f5755e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,12 @@ org.carlspring.strongbox strongbox-parent - 1.0-SNAPSHOT + 1.0-PR-62-SNAPSHOT strongbox-db pom - 1.0-SNAPSHOT + 1.0-PR-19-SNAPSHOT @@ -48,7 +48,7 @@ - strongbox-db-liquibase + strongbox-db-schema strongbox-db-import strongbox-db-server diff --git a/strongbox-db-import/pom.xml b/strongbox-db-import/pom.xml index 4d89a31..a22cbc8 100644 --- a/strongbox-db-import/pom.xml +++ b/strongbox-db-import/pom.xml @@ -8,37 +8,28 @@ org.carlspring.strongbox strongbox-db - 1.0-SNAPSHOT + 1.0-PR-19-SNAPSHOT ../pom.xml strongbox-db-import 2019 - - ${project.build.directory}/db - - ${project.groupId} - strongbox-db-liquibase + strongbox-db-server ${project.version} ${project.groupId} - strongbox-db-server + strongbox-db-schema ${project.version} - + org.springframework.boot - spring-boot-starter-jdbc - - - - com.orientechnologies - orientdb-jdbc + spring-boot-starter @@ -46,49 +37,9 @@ commons-beanutils - - - org.apache.ant - ant - 1.10.5 - - - org.liquibase - liquibase-core - - - org.apache.ant - ant - - - - - org.unbroken-dome.liquibase-orientdb - liquibase-orientdb - - - com.mattbertolini - liquibase-slf4j - - - org.hibernate.validator - hibernate-validator - - - - - org.apache.commons - commons-lang3 - - - - src/main/resources - true - - org.springframework.boot @@ -102,6 +53,7 @@ -Xmx1024m + --strongbox.db.janus-graph.storage-root=${project.build.directory}/db diff --git a/strongbox-db-import/src/main/assembly/db-schema.xml b/strongbox-db-import/src/main/assembly/db-schema.xml index 794716a..e2f794f 100644 --- a/strongbox-db-import/src/main/assembly/db-schema.xml +++ b/strongbox-db-import/src/main/assembly/db-schema.xml @@ -9,8 +9,8 @@ - ${project.build.directory}/db/strongbox - META-INF/resources/strongbox/db/strongbox + ${project.build.directory}/db + META-INF/org/carlsparing/strongbox/db \ No newline at end of file diff --git a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/Application.java b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/Application.java index 2bdd916..44c1460 100644 --- a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/Application.java +++ b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/Application.java @@ -2,12 +2,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; /** * @author Przemyslaw Fusik */ -@SpringBootApplication +@SpringBootApplication(exclude = ValidationAutoConfiguration.class) public class Application { diff --git a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/orient/OrientDbConfiguration.java b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/orient/OrientDbConfiguration.java deleted file mode 100644 index bff6c37..0000000 --- a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/orient/OrientDbConfiguration.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.carlspring.strongbox.db.orient; - -import java.lang.reflect.Field; -import java.util.Properties; - -import javax.sql.DataSource; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.util.ReflectionUtils; -import org.strongbox.db.server.EmbeddedOrientDbServer; -import org.strongbox.db.server.OrientDbServer; -import org.strongbox.db.server.OrientDbServerConfiguration; -import org.strongbox.db.server.OrientDbServerProperties; -import org.strongbox.db.server.OrientDbStudioConfiguration; -import org.strongbox.db.server.OrientDbStudioProperties; - -import com.orientechnologies.orient.core.db.ODatabasePool; -import com.orientechnologies.orient.core.db.ODatabaseType; -import com.orientechnologies.orient.core.db.OrientDB; -import com.orientechnologies.orient.core.db.OrientDBConfig; -import com.orientechnologies.orient.jdbc.OrientDataSource; - -/** - * @author Przemyslaw Fusik - */ -@Configuration -class OrientDbConfiguration -{ - - private static final Logger logger = LoggerFactory.getLogger(OrientDbConfiguration.class); - - @Bean - OrientDbServer orientDbServer(OrientDbServerConfiguration serverProperties, OrientDbStudioConfiguration studioProperties) { - return new EmbeddedOrientDbServer(studioProperties, serverProperties); - } - - @Bean - @ConfigurationProperties(prefix = "strongbox.orientdb.studio") - OrientDbStudioConfiguration orientDbStudioProperties() { - return new OrientDbStudioProperties(); - } - - @Bean - @ConfigurationProperties(prefix = "strongbox.orientdb.server") - OrientDbServerConfiguration orientDbServerProperties() { - return new OrientDbServerProperties(); - } - - @Bean(destroyMethod = "close") - @DependsOn("orientDbServer") - OrientDB orientDB(OrientDbServerConfiguration orientDbServerProperties) - { - OrientDB orientDB = new OrientDB(StringUtils.substringBeforeLast(orientDbServerProperties.getUrl(), "/"), - orientDbServerProperties.getUsername(), - orientDbServerProperties.getPassword(), - OrientDBConfig.defaultConfig()); - String database = orientDbServerProperties.getDatabase(); - - if (!orientDB.exists(database)) - { - logger.info(String.format("Creating database [%s]...", database)); - - orientDB.create(database, ODatabaseType.PLOCAL); - } - else - { - logger.info("Re-using existing database " + database + "."); - } - return orientDB; - } - - @Bean - @LiquibaseDataSource - DataSource dataSource(ODatabasePool pool, - OrientDB orientDB) - { - OrientDataSource ds = new OrientDataSource(orientDB); - - ds.setInfo(new Properties()); - - // DEV note: - // NPEx hotfix for OrientDataSource.java:134 :) - Field poolField = ReflectionUtils.findField(OrientDataSource.class, "pool"); - ReflectionUtils.makeAccessible(poolField); - ReflectionUtils.setField(poolField, ds, pool); - - return ds; - } - - @Bean(destroyMethod = "close") - ODatabasePool databasePool(OrientDB orientDB, OrientDbServerConfiguration orientDbServerProperties) - { - return new ODatabasePool(orientDB, - orientDbServerProperties.getDatabase(), - orientDbServerProperties.getUsername(), - orientDbServerProperties.getPassword()); - } - -} diff --git a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/schema/SchemaConfig.java b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/schema/SchemaConfig.java new file mode 100644 index 0000000..fad51c4 --- /dev/null +++ b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/schema/SchemaConfig.java @@ -0,0 +1,16 @@ +package org.carlspring.strongbox.db.schema; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SchemaConfig +{ + + @Bean + StrongboxSchema strongboxSchema() + { + return new StrongboxSchema(); + } + +} diff --git a/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/server/EmbeddedDbServerConfiguration.java b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/server/EmbeddedDbServerConfiguration.java new file mode 100644 index 0000000..b4bdc9b --- /dev/null +++ b/strongbox-db-import/src/main/java/org/carlspring/strongbox/db/server/EmbeddedDbServerConfiguration.java @@ -0,0 +1,53 @@ +package org.carlspring.strongbox.db.server; + +import org.janusgraph.core.JanusGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.strongbox.db.server.CassandraEmbeddedConfiguration; +import org.strongbox.db.server.CassandraEmbeddedProperties; +import org.strongbox.db.server.EmbeddedDbServer; +import org.strongbox.db.server.EmbeddedJanusGraphWithCassandraServer; +import org.strongbox.db.server.JanusGraphConfiguration; +import org.strongbox.db.server.JanusGraphProperties; + +/** + * @author Przemyslaw Fusik + * @author sbespalov + */ +@Configuration +class EmbeddedDbServerConfiguration +{ + + private static final Logger logger = LoggerFactory.getLogger(EmbeddedDbServerConfiguration.class); + + @Bean + EmbeddedDbServer embeddedDbServer(CassandraEmbeddedConfiguration cassandraConfiguration, + JanusGraphConfiguration janusGraphConfiguration) + { + return new EmbeddedJanusGraphWithCassandraServer(cassandraConfiguration, janusGraphConfiguration); + } + + @Bean + JanusGraph JanusGraph(EmbeddedDbServer server) + { + return ((EmbeddedJanusGraphWithCassandraServer) server).getJanusGraph(); + } + + @Bean + @ConfigurationProperties(prefix = "strongbox.db.janus-graph") + JanusGraphConfiguration janusGraphConfiguration() + { + return new JanusGraphProperties(); + } + + @Bean + CassandraEmbeddedConfiguration cassandraEmbeddedConfiguration(JanusGraphConfiguration janusGraphConfiguration) + { + return CassandraEmbeddedProperties.getInstance(janusGraphConfiguration.getStorageRoot(), + janusGraphConfiguration.getStoragePort()); + } + +} diff --git a/strongbox-db-import/src/main/resources/application.yml b/strongbox-db-import/src/main/resources/application.yml index 620fd9d..e48e1fb 100644 --- a/strongbox-db-import/src/main/resources/application.yml +++ b/strongbox-db-import/src/main/resources/application.yml @@ -1,23 +1,15 @@ spring: main: web-application-type: NONE - liquibase: - change-log: classpath:/db/changelog/db.changelog-master.xml strongbox: - orientdb: - server: - protocol: remote - host: 127.0.0.1 - port: "2025" - database: strongbox - username: admin - password: password - path: @strongbox.orientdb.path@ - studio: - ipAddress: 127.0.0.1 - port: 2480 - enabled: true - path: @strongbox.orientdb.path@ + db: + janus-graph: + #TODO: [First start] randomize + storage-username: cassandra + storage-password: cassandra + storage-host: 127.0.0.1 + storage-port: 49142 + storage-root: ./target/db logging: level: liquibase: INFO \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/db.changelog-master.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/db.changelog-master.xml deleted file mode 100644 index 6523632..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/db.changelog-master.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/db.changelog-1.0.0.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/db.changelog-1.0.0.xml deleted file mode 100644 index 5811154..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/db.changelog-1.0.0.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.10__ArtifactEntry.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.10__ArtifactEntry.xml deleted file mode 100644 index 3a29163..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.10__ArtifactEntry.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.11__ArtifactEntry_cleanup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.11__ArtifactEntry_cleanup.xml deleted file mode 100644 index e08ec53..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.11__ArtifactEntry_cleanup.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - artifactCoordinates IS NULL - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.12__Configuration_cleanup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.12__Configuration_cleanup.xml deleted file mode 100644 index 237a238..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.12__Configuration_cleanup.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.13__ArtifactEntry_path.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.13__ArtifactEntry_path.xml deleted file mode 100644 index 374ec08..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.13__ArtifactEntry_path.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.14__User_cleanup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.14__User_cleanup.xml deleted file mode 100644 index 05e66fb..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.14__User_cleanup.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.15__Authorization_cleanup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.15__Authorization_cleanup.xml deleted file mode 100644 index 656fd39..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.15__Authorization_cleanup.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.16__CronTaskConfiguration_cleanup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.16__CronTaskConfiguration_cleanup.xml deleted file mode 100644 index 717a529..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.16__CronTaskConfiguration_cleanup.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.17__ArtifactArchiveListing.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.17__ArtifactArchiveListing.xml deleted file mode 100644 index 9dab10f..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.17__ArtifactArchiveListing.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.18__ArtifactGroup.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.18__ArtifactGroup.xml deleted file mode 100644 index 76ec48e..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.18__ArtifactGroup.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.19__ArtifactEntry.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.19__ArtifactEntry.xml deleted file mode 100644 index c8291f1..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.19__ArtifactEntry.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.1__GenericEntity.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.1__GenericEntity.xml deleted file mode 100644 index 9c5f443..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.1__GenericEntity.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.20__User.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.20__User.xml deleted file mode 100644 index 3a15116..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.20__User.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.21__ArtifactEntry.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.21__ArtifactEntry.xml deleted file mode 100644 index fde581f..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.21__ArtifactEntry.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.22__UserEntry.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.22__UserEntry.xml deleted file mode 100644 index 4329f5c..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.22__UserEntry.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.2__ArtifactEntry.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.2__ArtifactEntry.xml deleted file mode 100644 index 14aaf94..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.2__ArtifactEntry.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.3__AuthorizationConfig.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.3__AuthorizationConfig.xml deleted file mode 100644 index 97f69be..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.3__AuthorizationConfig.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.4__BinaryConfiguration.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.4__BinaryConfiguration.xml deleted file mode 100644 index 7351386..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.4__BinaryConfiguration.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.5__CronTaskConfiguration.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.5__CronTaskConfiguration.xml deleted file mode 100644 index e6ed941..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.5__CronTaskConfiguration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.6__User.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.6__User.xml deleted file mode 100644 index fff2dd9..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.6__User.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.7__Configuration.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.7__Configuration.xml deleted file mode 100644 index bffbc8a..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.7__Configuration.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.8__AuthorizationConfig.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.8__AuthorizationConfig.xml deleted file mode 100644 index 43b3b9b..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.8__AuthorizationConfig.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.9__Repository.xml b/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.9__Repository.xml deleted file mode 100644 index 71e2de4..0000000 --- a/strongbox-db-liquibase/src/main/resources/db/changelog/v1.0.0/v1.0.0.9__Repository.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/strongbox-db-liquibase/pom.xml b/strongbox-db-schema/pom.xml similarity index 75% rename from strongbox-db-liquibase/pom.xml rename to strongbox-db-schema/pom.xml index 8a79119..8b3600b 100644 --- a/strongbox-db-liquibase/pom.xml +++ b/strongbox-db-schema/pom.xml @@ -8,18 +8,18 @@ org.carlspring.strongbox strongbox-db - 1.0-SNAPSHOT + 1.0-PR-19-SNAPSHOT ../pom.xml - strongbox-db-liquibase + strongbox-db-schema 2019 - com.google.guava - guava + org.janusgraph + janusgraph-core diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java new file mode 100644 index 0000000..bc01849 --- /dev/null +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java @@ -0,0 +1,12 @@ +package org.carlspring.strongbox.db.schema; + +public interface Edges +{ + + String ARTIFACT_HAS_ARTIFACT_COORDINATES = "ArtifactHasArtifactCoordinates"; + String ARTIFACT_HAS_TAGS = "ArtifactHasTags"; + String ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES = "ArtifactCoordinatesInheritGenericArtifactCoordinates"; + String ARTIFACT_GROUP_HAS_ARTIFACTS = "ArtifactGroupHasArtifacts"; + String REMOTE_ARTIFACT_INHERIT_ARTIFACT = "RemoteArtifactInheritArtifact"; + +} diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java new file mode 100644 index 0000000..e7addc7 --- /dev/null +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java @@ -0,0 +1,48 @@ +package org.carlspring.strongbox.db.schema; + +/** + * @author ankit.tomar + */ +public interface Properties +{ + + String UUID = "uuid"; + String STORAGE_ID = "storageId"; + String REPOSITORY_ID = "repositoryId"; + String NAME = "name"; + String LAST_UPDATED = "lastUpdated"; + String SIZE_IN_BYTES = "sizeInBytes"; + String LAST_USED = "lastUsed"; + String CREATED = "created"; + String DOWNLOAD_COUNT = "downloadCount"; + String CHECKSUMS = "checksums"; + String CACHED = "cached"; + String VERSION = "version"; + String USERNAME = "username"; + String PASSWORD = "password"; + String ENABLED = "enabled"; + String ROLES = "roles"; + String SECURITY_TOKEN_KEY = "securityTokenKey"; + String SOURCE_ID = "sourceId"; + String FILE_NAMES = "filenames"; + String COORDINATES_EXTENSION = "coordinates.extension"; + String COORDINATES_NAME = "coordinates.name"; + String COORDINATES_GROUP_ID = "coordinates.groupId"; + String COORDINATES_ARTIFACT_ID = "coordinates.artifactId"; + String COORDINATES_CLASSIFIER = "coordinates.classifier"; + String COORDINATES_SCOPE = "coordinates.scope"; + String COORDINATES_ID = "coordinates.id"; + String COORDINATES_FILENAME = "coordinates.filename"; + String COORDINATES_BUILD = "coordinates.build"; + String COORDINATES_ABI = "coordinates.abi"; + String COORDINATES_PLATFORM = "coordinates.platform"; + String COORDINATES_PACKAGING = "coordinates.packaging"; + String COORDINATES_DISTRIBUTION = "coordinates.distribution"; + String COORDINATES_PATH = "coordinates.path"; + String COORDINATES_BASE_NAME = "coordinates.base_name"; + String COORDINATES_RELEASE = "coordinates.release"; + String COORDINATES_ARCHITECTURE = "coordinates.architecture"; + String COORDINATES_PACKAGE_TYPE = "coordinates.package_type"; + String COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION = "coordinates.languageImplementationVersion"; + +} diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java new file mode 100644 index 0000000..cd7485b --- /dev/null +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java @@ -0,0 +1,615 @@ +package org.carlspring.strongbox.db.schema; + +import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_GROUP_HAS_ARTIFACTS; +import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_HAS_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_HAS_TAGS; +import static org.carlspring.strongbox.db.schema.Edges.REMOTE_ARTIFACT_INHERIT_ARTIFACT; +import static org.carlspring.strongbox.db.schema.Properties.CACHED; +import static org.carlspring.strongbox.db.schema.Properties.CHECKSUMS; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_ABI; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_ARCHITECTURE; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_ARTIFACT_ID; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_BASE_NAME; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_BUILD; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_CLASSIFIER; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_DISTRIBUTION; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_EXTENSION; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_FILENAME; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_GROUP_ID; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_ID; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_NAME; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_PACKAGE_TYPE; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_PACKAGING; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_PATH; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_PLATFORM; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_RELEASE; +import static org.carlspring.strongbox.db.schema.Properties.COORDINATES_SCOPE; +import static org.carlspring.strongbox.db.schema.Properties.CREATED; +import static org.carlspring.strongbox.db.schema.Properties.DOWNLOAD_COUNT; +import static org.carlspring.strongbox.db.schema.Properties.ENABLED; +import static org.carlspring.strongbox.db.schema.Properties.FILE_NAMES; +import static org.carlspring.strongbox.db.schema.Properties.LAST_UPDATED; +import static org.carlspring.strongbox.db.schema.Properties.LAST_USED; +import static org.carlspring.strongbox.db.schema.Properties.NAME; +import static org.carlspring.strongbox.db.schema.Properties.PASSWORD; +import static org.carlspring.strongbox.db.schema.Properties.REPOSITORY_ID; +import static org.carlspring.strongbox.db.schema.Properties.ROLES; +import static org.carlspring.strongbox.db.schema.Properties.SECURITY_TOKEN_KEY; +import static org.carlspring.strongbox.db.schema.Properties.SIZE_IN_BYTES; +import static org.carlspring.strongbox.db.schema.Properties.SOURCE_ID; +import static org.carlspring.strongbox.db.schema.Properties.STORAGE_ID; +import static org.carlspring.strongbox.db.schema.Properties.UUID; +import static org.carlspring.strongbox.db.schema.Properties.VERSION; +import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT; +import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_ID_GROUP; +import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_TAG; +import static org.carlspring.strongbox.db.schema.Vertices.GENERIC_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.MAVEN_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.NPM_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.NUGET_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.PYPI_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.RAW_ARTIFACT_COORDINATES; +import static org.carlspring.strongbox.db.schema.Vertices.REMOTE_ARTIFACT; +import static org.carlspring.strongbox.db.schema.Vertices.USER; +import static org.janusgraph.core.Multiplicity.MANY2ONE; +import static org.janusgraph.core.Multiplicity.MULTI; +import static org.janusgraph.core.Multiplicity.ONE2MANY; +import static org.janusgraph.core.Multiplicity.ONE2ONE; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.Cardinality; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.Multiplicity; +import org.janusgraph.core.PropertyKey; +import org.janusgraph.core.VertexLabel; +import org.janusgraph.core.schema.ConsistencyModifier; +import org.janusgraph.core.schema.EdgeLabelMaker; +import org.janusgraph.core.schema.JanusGraphIndex; +import org.janusgraph.core.schema.JanusGraphManagement; +import org.janusgraph.core.schema.JanusGraphManagement.IndexBuilder; +import org.janusgraph.core.schema.JanusGraphSchemaType; +import org.janusgraph.core.schema.PropertyKeyMaker; +import org.janusgraph.core.schema.SchemaAction; +import org.janusgraph.graphdb.database.management.ManagementSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StrongboxSchema +{ + + private static final Logger logger = LoggerFactory.getLogger(StrongboxSchema.class); + + public void createSchema(JanusGraph jg) + throws InterruptedException + { + JanusGraphManagement jgm = jg.openManagement(); + try + { + applySchemaChanges(jgm); + jgm.commit(); + } + catch (Exception e) + { + logger.error("Failed to apply schema changes.", e); + jgm.rollback(); + throw new RuntimeException("Failed to apply schema changes.", e); + } + + jgm = jg.openManagement(); + Set indexes; + try + { + indexes = createIndexes(jg, jgm); + jgm.commit(); + } + catch (Exception e) + { + logger.error("Failed to create indexes.", e); + jgm.rollback(); + throw new RuntimeException("Failed to create indexes.", e); + } + + for (String janusGraphIndex : indexes) + { + logger.info(String.format("Wait index [%s] to be registered.", janusGraphIndex)); + ManagementSystem.awaitGraphIndexStatus(jg, janusGraphIndex).call(); + } + + jgm = jg.openManagement(); + try + { + enableIndexes(jgm, indexes); + jgm.commit(); + } + catch (Exception e) + { + logger.error("Failed to enable indexes.", e); + jgm.rollback(); + throw new RuntimeException("Failed to enable indexes.", e); + } + + jgm = jg.openManagement(); + try + { + logger.info(String.format("Schema: %n%s", jgm.printSchema())); + } + finally + { + jgm.rollback(); + } + } + + protected void enableIndexes(JanusGraphManagement jgm, + Set indexes) + throws InterruptedException, + ExecutionException + { + for (String janusGraphIndex : indexes) + { + logger.info(String.format("Enabling index [%s].", janusGraphIndex)); + jgm.updateIndex(jgm.getGraphIndex(janusGraphIndex), SchemaAction.ENABLE_INDEX).get(); + } + } + + protected Set createIndexes(JanusGraph jg, + JanusGraphManagement jgm) + throws InterruptedException + { + Set result = new HashSet<>(); + + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(REMOTE_ARTIFACT), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(RAW_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(MAVEN_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(NPM_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(NUGET_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(PYPI_ARTIFACT_COORDINATES), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_TAG), + true, + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_ID_GROUP), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_ID_GROUP), + false, + false, + jgm.getPropertyKey(STORAGE_ID), + jgm.getPropertyKey(REPOSITORY_ID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_ID_GROUP), + true, + jgm.getPropertyKey(STORAGE_ID), + jgm.getPropertyKey(REPOSITORY_ID), + jgm.getPropertyKey(NAME)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(USER), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); + + return result; + } + + private void applySchemaChanges(JanusGraphManagement jgm) + { + // Properties + createProperties(jgm); + + // Vertices + makeVertexLabelIfDoesNotExist(jgm, ARTIFACT); + makeVertexLabelIfDoesNotExist(jgm, REMOTE_ARTIFACT); + makeVertexLabelIfDoesNotExist(jgm, GENERIC_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, RAW_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, MAVEN_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, NPM_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, NUGET_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, PYPI_ARTIFACT_COORDINATES); + makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_TAG); + makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_ID_GROUP); + makeVertexLabelIfDoesNotExist(jgm, USER); + + // Edges + makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_HAS_ARTIFACT_COORDINATES, MANY2ONE); + makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_HAS_TAGS, MULTI); + makeEdgeLabelIfDoesNotExist(jgm, REMOTE_ARTIFACT_INHERIT_ARTIFACT, ONE2ONE); + makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES, ONE2ONE); + makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_GROUP_HAS_ARTIFACTS, ONE2MANY); + + // Add property constraints + applyPropertyConstraints(jgm); + + // Add connection constraints + applyConnectionConstraints(jgm); + } + + private void applyConnectionConstraints(JanusGraphManagement jgm) + { + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_HAS_ARTIFACT_COORDINATES), + jgm.getVertexLabel(ARTIFACT), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_HAS_TAGS), + jgm.getVertexLabel(ARTIFACT), + jgm.getVertexLabel(ARTIFACT_TAG)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_GROUP_HAS_ARTIFACTS), + jgm.getVertexLabel(ARTIFACT_ID_GROUP), + jgm.getVertexLabel(ARTIFACT)); + + jgm.addConnection(jgm.getEdgeLabel(REMOTE_ARTIFACT_INHERIT_ARTIFACT), + jgm.getVertexLabel(REMOTE_ARTIFACT), + jgm.getVertexLabel(ARTIFACT)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES), + jgm.getVertexLabel(RAW_ARTIFACT_COORDINATES), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES), + jgm.getVertexLabel(NUGET_ARTIFACT_COORDINATES), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES), + jgm.getVertexLabel(NPM_ARTIFACT_COORDINATES), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES), + jgm.getVertexLabel(MAVEN_ARTIFACT_COORDINATES), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES), + jgm.getVertexLabel(PYPI_ARTIFACT_COORDINATES), + jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + + } + + private void applyPropertyConstraints(JanusGraphManagement jgm) + { + // Vertex Property Constraints + addVertexPropertyConstraints(jgm, + ARTIFACT, + UUID, + STORAGE_ID, + REPOSITORY_ID, + CREATED, + LAST_UPDATED, + LAST_USED, + SIZE_IN_BYTES, + DOWNLOAD_COUNT, + FILE_NAMES, + CHECKSUMS); + + addVertexPropertyConstraints(jgm, + REMOTE_ARTIFACT, + UUID, + CACHED, + CREATED); + + addVertexPropertyConstraints(jgm, + GENERIC_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_ID, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_PATH, + COORDINATES_SCOPE, + COORDINATES_GROUP_ID, + COORDINATES_ARTIFACT_ID, + COORDINATES_CLASSIFIER, + COORDINATES_DISTRIBUTION, + COORDINATES_BUILD, + COORDINATES_ABI, + COORDINATES_PLATFORM, + COORDINATES_PACKAGING, + COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION, + CREATED); + + addVertexPropertyConstraints(jgm, + RAW_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_PATH, + CREATED); + + addVertexPropertyConstraints(jgm, + MAVEN_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_GROUP_ID, + COORDINATES_ARTIFACT_ID, + COORDINATES_CLASSIFIER, + CREATED); + + addVertexPropertyConstraints(jgm, + NPM_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_SCOPE, + CREATED); + + addVertexPropertyConstraints(jgm, + NUGET_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_ID, + CREATED); + + addVertexPropertyConstraints(jgm, + PYPI_ARTIFACT_COORDINATES, + UUID, + VERSION, + COORDINATES_EXTENSION, + COORDINATES_NAME, + COORDINATES_BUILD, + COORDINATES_ABI, + COORDINATES_PLATFORM, + COORDINATES_PACKAGING, + COORDINATES_DISTRIBUTION, + COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION, + CREATED); + + addVertexPropertyConstraints(jgm, + ARTIFACT_TAG, + UUID, + CREATED); + + addVertexPropertyConstraints(jgm, + ARTIFACT_ID_GROUP, + UUID, + STORAGE_ID, + REPOSITORY_ID, + NAME, + CREATED); + + addVertexPropertyConstraints(jgm, + USER, + UUID, + PASSWORD, + ENABLED, + ROLES, + SECURITY_TOKEN_KEY, + SOURCE_ID, + CREATED, + LAST_UPDATED); + } + + private void addVertexPropertyConstraints(JanusGraphManagement jgm, + String vertex, + String... propertykeys) + { + VertexLabel vertexLabel = jgm.getVertexLabel(vertex); + for (String propertyKey : propertykeys) + { + jgm.addProperties(vertexLabel, jgm.getPropertyKey(propertyKey)); + } + } + + private void createProperties(JanusGraphManagement jgm) + { + makePropertyKeyIfDoesNotExist(jgm, UUID, + String.class).ifPresent(p -> jgm.setConsistency(p, ConsistencyModifier.LOCK)); + makePropertyKeyIfDoesNotExist(jgm, STORAGE_ID, String.class); + makePropertyKeyIfDoesNotExist(jgm, REPOSITORY_ID, String.class); + makePropertyKeyIfDoesNotExist(jgm, NAME, String.class); + makePropertyKeyIfDoesNotExist(jgm, LAST_UPDATED, Long.class, Cardinality.SINGLE); + + // Artifact + makePropertyKeyIfDoesNotExist(jgm, SIZE_IN_BYTES, Long.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, LAST_USED, Long.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, CREATED, Long.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, DOWNLOAD_COUNT, Integer.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, FILE_NAMES, String.class, Cardinality.SET); + makePropertyKeyIfDoesNotExist(jgm, CHECKSUMS, String.class, Cardinality.SET); + + // RemoteArtifact + makePropertyKeyIfDoesNotExist(jgm, CACHED, Boolean.class, Cardinality.SINGLE); + + // Common coordinates + makePropertyKeyIfDoesNotExist(jgm, VERSION, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_EXTENSION, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_NAME, String.class, Cardinality.SINGLE); + + // Maven + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_GROUP_ID, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_ARTIFACT_ID, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_CLASSIFIER, String.class, Cardinality.SINGLE); + + // Npm + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_SCOPE, String.class, Cardinality.SINGLE); + + // Nuget + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_ID, String.class, Cardinality.SINGLE); + + // P2 + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_FILENAME, String.class, Cardinality.SINGLE); + + // Pypi + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_BUILD, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION, String.class, + Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_ABI, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_PLATFORM, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_PACKAGING, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_DISTRIBUTION, String.class, Cardinality.SINGLE); + + // Raw + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_PATH, String.class, Cardinality.SINGLE); + + // Rpm + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_BASE_NAME, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_RELEASE, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_ARCHITECTURE, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, COORDINATES_PACKAGE_TYPE, String.class, Cardinality.SINGLE); + + // User + makePropertyKeyIfDoesNotExist(jgm, PASSWORD, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, ENABLED, Boolean.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, ROLES, String.class, Cardinality.SET); + makePropertyKeyIfDoesNotExist(jgm, SECURITY_TOKEN_KEY, String.class, Cardinality.SINGLE); + makePropertyKeyIfDoesNotExist(jgm, SOURCE_ID, String.class, Cardinality.SINGLE); + } + + private Optional buildIndexIfNecessary(final JanusGraphManagement jgm, + final Class elementType, + final JanusGraphSchemaType schemaType, + final boolean unique, + final PropertyKey... properties){ + return buildIndexIfNecessary(jgm, elementType, schemaType, unique, true, properties); + } + + private Optional buildIndexIfNecessary(final JanusGraphManagement jgm, + final Class elementType, + final JanusGraphSchemaType schemaType, + final boolean unique, + final boolean consistent, + final PropertyKey... properties) + { + final String name = schemaType.name() + "By" + + Arrays.stream(properties) + .map(PropertyKey::name) + .map(StringUtils::capitalize) + .reduce((p1, + p2) -> p1 + "And" + p2) + .get(); + if (jgm.containsGraphIndex(name)) + { + return Optional.empty(); + } + + IndexBuilder indexBuilder = jgm.buildIndex(name, elementType) + .indexOnly(schemaType); + Arrays.stream(properties).forEach(indexBuilder::addKey); + + if (unique) + { + indexBuilder = indexBuilder.unique(); + } + JanusGraphIndex intex = indexBuilder.buildCompositeIndex(); + if (consistent) + { + jgm.setConsistency(intex, ConsistencyModifier.LOCK); + } + + return Optional.of(intex.name()); + } + + private void makeEdgeLabelIfDoesNotExist(final JanusGraphManagement jgm, + final String name, + final Multiplicity multiplicity) + { + if (jgm.containsEdgeLabel(name)) + { + return; + } + EdgeLabelMaker edgeLabelMaker = jgm.makeEdgeLabel(name); + if (multiplicity != null) + { + edgeLabelMaker = edgeLabelMaker.multiplicity(multiplicity); + } + + edgeLabelMaker.make(); + } + + private void makeVertexLabelIfDoesNotExist(final JanusGraphManagement jgm, + final String name) + { + if (jgm.containsVertexLabel(name)) + { + return; + } + jgm.makeVertexLabel(name).make(); + } + + private Optional makePropertyKeyIfDoesNotExist(final JanusGraphManagement jgm, + final String name, + final Class dataType) + { + return makePropertyKeyIfDoesNotExist(jgm, name, dataType, null); + } + + private Optional makePropertyKeyIfDoesNotExist(final JanusGraphManagement jgm, + final String name, + final Class dataType, + final Cardinality cardinality) + { + if (jgm.containsPropertyKey(name)) + { + return Optional.empty(); + } + + PropertyKeyMaker propertyKeyMaker = jgm.makePropertyKey(name).dataType(dataType); + if (cardinality != null) + { + propertyKeyMaker = propertyKeyMaker.cardinality(cardinality); + } + return Optional.of(propertyKeyMaker.make()); + } + +} diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java new file mode 100644 index 0000000..1e2cc25 --- /dev/null +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java @@ -0,0 +1,19 @@ +package org.carlspring.strongbox.db.schema; + +public interface Vertices +{ + + String ARTIFACT = "Artifact"; + String REMOTE_ARTIFACT = "RemoteArtifact"; + String GENERIC_ARTIFACT_COORDINATES = "GenericArtifactCoordinates"; + String ARTIFACT_COORDINATES = "ArtifactCoordinates"; + String RAW_ARTIFACT_COORDINATES = "RawArtifactCoordinates"; + String MAVEN_ARTIFACT_COORDINATES = "MavenArtifactCoordinates"; + String NPM_ARTIFACT_COORDINATES = "NpmArtifactCoordinates"; + String NUGET_ARTIFACT_COORDINATES = "NugetArtifactCoordinates"; + String PYPI_ARTIFACT_COORDINATES = "PypiArtifactCoordinates"; + String RPM_ARTIFACT_COORDINATES = "RpmArtifactCoordinates"; + String ARTIFACT_TAG = "ArtifactTag"; + String ARTIFACT_ID_GROUP = "ArtifactIdGroup"; + String USER = "User"; +} diff --git a/strongbox-db-server/pom.xml b/strongbox-db-server/pom.xml index 9a40a73..bb7ad65 100644 --- a/strongbox-db-server/pom.xml +++ b/strongbox-db-server/pom.xml @@ -9,7 +9,7 @@ org.carlspring.strongbox strongbox-db - 1.0-SNAPSHOT + 1.0-PR-19-SNAPSHOT ../pom.xml @@ -22,36 +22,74 @@ UTF-8 - + com.fasterxml.jackson.core jackson-databind - com.orientechnologies - orientdb-core + org.janusgraph + janusgraph-cql - com.orientechnologies - orientdb-client + org.janusgraph + janusgraph-inmemory - com.orientechnologies - orientdb-server + org.apache.cassandra + cassandra-all - com.orientechnologies - orientdb-object + io.dropwizard.metrics + metrics-core + + + org.apache.tinkerpop + gremlin-core + + + org.apache.tinkerpop + gremlin-driver + + + org.apache.tinkerpop + gremlin-server + + + org.apache.tinkerpop + gremlin-groovy + + - com.orientechnologies - orientdb-graphdb + org.opencypher.gremlin + cypher-gremlin-neo4j-driver + + org.opencypher.gremlin + cypher-gremlin-server-plugin + + + + org.neo4j.driver + neo4j-java-driver + + + org.neo4j + neo4j-ogm-api + + + - - org.webjars - orientdb-studio + + commons-lang + commons-lang + 2.6 + diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/CypherGremlinStatementRunner.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/CypherGremlinStatementRunner.java new file mode 100644 index 0000000..093dad1 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/CypherGremlinStatementRunner.java @@ -0,0 +1,168 @@ +package org.opencypher.gremlin.neo4j.driver; + +import static org.opencypher.gremlin.translation.ReturnProperties.ID; +import static org.opencypher.gremlin.translation.ReturnProperties.INV; +import static org.opencypher.gremlin.translation.ReturnProperties.LABEL; +import static org.opencypher.gremlin.translation.ReturnProperties.OUTV; +import static org.opencypher.gremlin.translation.ReturnProperties.RELATIONSHIP_TYPE; +import static org.opencypher.gremlin.translation.ReturnProperties.TYPE; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CompletionStage; + +import org.janusgraph.core.JanusGraphTransaction; +import org.janusgraph.core.JanusGraphVertex; +import org.janusgraph.graphdb.relations.RelationIdentifier; +import org.neo4j.driver.v1.Record; +import org.neo4j.driver.v1.Session; +import org.neo4j.driver.v1.Statement; +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.driver.v1.StatementResultCursor; +import org.neo4j.driver.v1.StatementRunner; +import org.neo4j.driver.v1.Value; +import org.neo4j.driver.v1.types.TypeSystem; +import org.opencypher.gremlin.client.CypherGremlinClient; +import org.opencypher.gremlin.neo4j.ogm.transaction.GremlinTransaction; +import org.strongbox.util.Commons; + +/** + * @author sbespalov + */ +public class CypherGremlinStatementRunner implements StatementRunner +{ + + private final Session session; + private final GremlinTransaction gremlinTransaction; + + public CypherGremlinStatementRunner(GremlinTransaction gremlinTransaction) + { + this.gremlinTransaction = gremlinTransaction; + this.session = new GremlinServerSession(null, + CypherGremlinClient.inMemory(gremlinTransaction.getNativeTransaction().traversal()), + new JanusGraphValueConverter(false)); + } + + public StatementResult run(String statementTemplate, + Value parameters) + { + return session.run(statementTemplate, parameters); + } + + public StatementResult run(String statementTemplate, + Map statementParameters) + { + return session.run(statementTemplate, statementParameters); + } + + public StatementResult run(String statementTemplate, + Record statementParameters) + { + return session.run(statementTemplate, statementParameters); + } + + public StatementResult run(String statementTemplate) + { + return session.run(statementTemplate); + } + + public StatementResult run(Statement statement) + { + return session.run(statement); + } + + public TypeSystem typeSystem() + { + return session.typeSystem(); + } + + class JanusGraphValueConverter extends GremlinCypherValueConverter + { + + public JanusGraphValueConverter(boolean ignoreIds) + { + super(ignoreIds); + } + + @Override + Record toRecord(Map map) + { + map.entrySet().forEach(this::normalizeValue); + + return super.toRecord(map); + } + + private void normalizeValue(Entry e) + { + Object value = e.getValue(); + if (value instanceof Map) + { + Map nodeMap = (Map) value; + nodeMap.entrySet().forEach(this::normalizeValue); + } + else if (e.getValue() instanceof RelationIdentifier) + { + JanusGraphTransaction tx = (JanusGraphTransaction) gremlinTransaction.getNativeTransaction(); + RelationIdentifier relationIdentifier = (RelationIdentifier) e.getValue(); + if (ID.equals(e.getKey())) + { + e.setValue(relationIdentifier.getRelationId()); + + return; + } + + Map expectedValue = new HashMap<>(); + expectedValue.put(TYPE, RELATIONSHIP_TYPE); + expectedValue.put(ID, relationIdentifier.getRelationId()); + expectedValue.put(OUTV, relationIdentifier.getOutVertexId()); + expectedValue.put(INV, relationIdentifier.getInVertexId()); + + JanusGraphVertex typeVertex = tx.getVertex(relationIdentifier.getTypeId()); + expectedValue.put(LABEL, typeVertex.label()); + + e.setValue(expectedValue); + } + else if (value instanceof Date) + { + e.setValue(Commons.toLocalDateTime((Date) value)); + } + } + + } + + @Override + public CompletionStage runAsync(String statementTemplate, + Value parameters) + { + throw new UnsupportedOperationException(); + } + + @Override + public CompletionStage runAsync(String statementTemplate, + Map statementParameters) + { + throw new UnsupportedOperationException(); + } + + @Override + public CompletionStage runAsync(String statementTemplate, + Record statementParameters) + { + throw new UnsupportedOperationException(); + } + + @Override + public CompletionStage runAsync(String statementTemplate) + { + throw new UnsupportedOperationException(); + } + + @Override + public CompletionStage runAsync(Statement statement) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/Neo4jDriverEntityAdapter.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/Neo4jDriverEntityAdapter.java new file mode 100644 index 0000000..8b17ed2 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/driver/Neo4jDriverEntityAdapter.java @@ -0,0 +1,117 @@ +package org.opencypher.gremlin.neo4j.driver; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.v1.Value; +import org.neo4j.driver.v1.types.Entity; +import org.neo4j.driver.v1.types.Node; +import org.neo4j.driver.v1.types.Path; +import org.neo4j.driver.v1.types.Relationship; +import org.neo4j.ogm.driver.TypeSystem.NoNativeTypes; + +/** + * @author sbespalov + */ +public class Neo4jDriverEntityAdapter +{ + + public boolean isPath(Object value) + { + return value instanceof Path; + } + + public boolean isNode(Object value) + { + return value instanceof Node; + } + + public boolean isRelationship(Object value) + { + return value instanceof Relationship; + } + + public long nodeId(Object node) + { + return ((Node) node).id(); + } + + public List labels(Object value) + { + Node node = (Node) value; + List labels = new ArrayList<>(); + for (String label : node.labels()) + { + labels.add(label); + } + return labels; + } + + public long relationshipId(Object relationship) + { + return ((Relationship) relationship).id(); + } + + public String relationshipType(Object relationship) + { + return ((Relationship) relationship).type(); + } + + public Long startNodeId(Object relationship) + { + return ((Relationship) relationship).startNodeId(); + } + + public Long endNodeId(Object relationship) + { + return ((Relationship) relationship).endNodeId(); + } + + public Map properties(Object container) + { + return ((Entity) container).asMap(this::toMapped); + } + + public List nodesInPath(Object pathValue) + { + Path path = (Path) pathValue; + List nodes = new ArrayList<>(path.length()); + for (Node node : path.nodes()) + { + nodes.add(node); + } + return nodes; + } + + public List relsInPath(Object pathValue) + { + Path path = (Path) pathValue; + List rels = new ArrayList<>(path.length()); + for (Relationship rel : path.relationships()) + { + rels.add(rel); + } + return rels; + } + + private Object toMapped(Value value) + { + + if (value == null) + { + return null; + } + + if (value instanceof ListValue) + { + return value.asList(this::toMapped); + } + + Object object = value.asObject(); + return NoNativeTypes.INSTANCE.getNativeToMappedTypeAdapter(object.getClass()) + .apply(object); + } + +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/CypherQueryUtils.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/CypherQueryUtils.java new file mode 100644 index 0000000..f2b9c65 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/CypherQueryUtils.java @@ -0,0 +1,294 @@ +package org.opencypher.gremlin.neo4j.ogm; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.strongbox.util.Commons; + +public class CypherQueryUtils +{ + + private static final String UNWIND = "UNWIND"; + private static final String MERGE = "MERGE"; + private static final String MATCH = "MATCH"; + private static final String WHERE = "WHERE"; + private static final String WITH = "WITH"; + private static final String SET = "SET"; + private static final String RETURN = "RETURN"; + + private static final Logger logger = LoggerFactory.getLogger(CypherQueryUtils.class); + + public static String inlineParameters(String cypherStatement, + Map parameterMap) + { + String placeholderFormat; + Map params; + Collection> rows = (Collection>) parameterMap.get("rows"); + if (rows == null || rows.size() == 0) + { + // regular case + placeholderFormat = "$%s"; + params = parameterMap; + } + else + { + // the case with `UNWIND {rows} as row` + placeholderFormat = "row.%s"; + params = rows.iterator().next(); + } + + List> props = params.entrySet() + .stream() + .flatMap(CypherQueryUtils::flatten) + .collect(Collectors.toList()); + + for (Pair p : props) + { + cypherStatement = cypherStatement.replace(String.format(placeholderFormat, p.getKey()), + inlinedValue(p.getKey(), p.getValue())); + } + + return cypherStatement; + } + + private static String inlinedValue(String key, Object value) + { + if (value instanceof Number) + { + return value.toString(); + } + else if (value instanceof LocalDateTime) + { + return String.valueOf(Commons.toLong((LocalDateTime) value)); + } + else if (value instanceof Collection) { + return "[" + ((Collection)value).stream().map(e-> inlinedValue(key, e)).collect(Collectors.joining(",")) + "]"; + } + else if (value == null) + { + return "null"; + } + + return "'" + value.toString() + "'"; + } + + private static Stream> flatten(Map.Entry root) + { + if (root.getValue() instanceof Map) + { + return ((Map) root.getValue()).entrySet() + .stream() + .map(e -> Pair.of(root.getKey() + "." + e.getKey(), + e.getValue())) + .flatMap(CypherQueryUtils::flatten); + } + return Stream.of(Pair.of(root.getKey(), root.getValue())); + } + + public static String normalizeMergeByIdWithParams(String cypherStatement, + Map props) + { + if (!cypherStatement.startsWith(UNWIND)) + { + return cypherStatement; + } + + // cleanup multiple labels for DomainEntity inheritance + // TODO: make it generic + cypherStatement = cypherStatement.replace(String.format(":`%s`", "DomainEntity"), ""); + + int mergeIndex = cypherStatement.indexOf(MERGE); + int setIndex = cypherStatement.indexOf(SET); + int returnIndex = cypherStatement.indexOf(RETURN); + + if (mergeIndex < 0 || setIndex < 0 || returnIndex < 0) + { + return cypherStatement; + } + if (mergeIndex >= setIndex || setIndex >= returnIndex) + { + // not a MERGE statement + return cypherStatement; + } + + String mergeClause = cypherStatement.substring(mergeIndex, setIndex); + String setClause = cypherStatement.substring(setIndex, returnIndex); + + String alias; + if (mergeClause.contains("n:")) + { + alias = "n"; + } + else if (mergeClause.contains("rel:")) + { + alias = "rel"; + } + else + { + throw new IllegalArgumentException(String.format("Failet to parse node alias from [%s].", mergeClause)); + } + + // specify concrete properties to set + String propsClause = props.keySet() + .stream() + .map(p -> String.format("%s.%s = row.props.%s", alias, p, p)) + .reduce((p1, + p2) -> p1 + "," + p2) + .get(); + + return cypherStatement.replace(setClause, "SET " + propsClause + " "); + } + + public static String normalizeMatchByIdWithRelationResult(String cypherStatement, + String id) + { + // MATCH (n:`RepositoryArtifactIdGroup`) + // WHERE n.`uuid` = { id } + // WITH n + // RETURN n, + // _______[ + // _________[ + // ___________(n)-[r_r1:`RepositoryArtifactIdGroupEntity_ArtifactGroupEntity`]->(a1:`ArtifactGroup`) + // ___________| [ r_r1, a1 ] + // _________] + // _______] + if (!cypherStatement.startsWith(MATCH)) + { + return cypherStatement; + } + + int whereIndex = cypherStatement.indexOf(WHERE); + int withIndex = cypherStatement.indexOf(WITH); + int returnIndex = cypherStatement.indexOf(RETURN); + + if (whereIndex < 0 || withIndex < 0 || returnIndex < 0) + { + return cypherStatement; + } + + if (whereIndex >= withIndex || withIndex >= returnIndex) + { + return cypherStatement; + } + + String matchClause = cypherStatement.substring(0, whereIndex); + String whereClause = cypherStatement.substring(whereIndex, withIndex); + String withClause = cypherStatement.substring(withIndex, returnIndex); + String returnClause = cypherStatement.substring(returnIndex); + + logger.trace(String.format("Cyphter With: %s", withClause)); + logger.trace(String.format("Cyphter Return: %s", returnClause)); + + String returnResults = returnClause.replace(RETURN, ""); + List returnTokens = new ArrayList<>(); + for (int i = 0, j = returnResults.indexOf(","); j > 0; i = j + 1, j = returnResults.indexOf(",", i)) + { + String returnToken = returnResults.substring(i, j).trim(); + if (returnToken.startsWith("[")) + { + // this is relations query result token, which is commonly the + // last + returnTokens.add(returnResults.substring(i).trim()); + break; + } + else + { + // this is just a regular return token like some alias + returnTokens.add(returnToken); + } + } + + // return relations subquery should be last element + String returnRelationsClause = returnTokens.get(returnTokens.size() - 1); + returnTokens.remove(returnTokens.size() - 1); + if (!returnRelationsClause.startsWith("[") && !returnRelationsClause.endsWith("]")) + { + logger.trace("Return clause without relations."); + + return cypherStatement; + } + + // Remove list outer brackets + returnRelationsClause = returnRelationsClause.substring(1, returnRelationsClause.length() - 1).trim(); + // Remove frist bracket for first element and last bracket for last + // element + returnRelationsClause = returnRelationsClause.substring(1, returnRelationsClause.length() - 1).trim(); + + Map withRelations = new HashMap<>(); + for (String returnRelationClause : returnRelationsClause.split("\\]\\s*,\\s*\\[")) + { + if (!returnRelationClause.contains("|")) + { + throw new RuntimeException("Relation pattern not match."); + } + + String[] returnRelationTokens = returnRelationClause.split("\\|"); + if (returnRelationTokens.length != 2) + { + throw new RuntimeException("Return relation pattern not match."); + } + + String relationQueryResult = returnRelationTokens[1].trim(); + // remove brackets + relationQueryResult = relationQueryResult.substring(1, relationQueryResult.length() - 1).trim(); + // map relation results into relation query + withRelations.put(relationQueryResult.trim(), returnRelationTokens[0].trim()); + } + + withClause = withRelations.entrySet() + .stream() + .map(e -> { + String relationQueryResult = e.getKey(); + Arrays.stream(relationQueryResult.split(",")).forEach(r -> returnTokens.add(r)); + String localWith = returnTokens.stream() + .reduce((r1, + r2) -> r1.trim() + ", " + r2.trim()) + .get(); + return MATCH + " " + e.getValue() + " " + WITH + " " + localWith; + }) + .reduce((w1, + w2) -> w1 + " " + w2) + .get(); + + returnClause = RETURN + " " + returnTokens.stream() + .reduce((r1, + r2) -> r1.trim() + ", " + r2.trim()) + .get(); + + cypherStatement = matchClause + " " + whereClause + " " + WITH + " n " + withClause + " " + returnClause; + cypherStatement = cypherStatement.replace("{ id }", "'" + id + "'"); + return cypherStatement; + } + + public static void main(String[] args) + { + String query = "MATCH (n:`RepositoryArtifactIdGroup`) " + + "WHERE n.`uuid` = { id } " + + "WITH n " + + "RETURN n, " + + "[ [ (n)-[r_r1:`RepositoryArtifactIdGroupEntity_ArtifactGroupEntity`]->(a1:`ArtifactGroup`) | [ r_r1, a1 ] ], " + + + " [ (n)-[r_r2:`RepositoryArtifactIdGroupEntity_ArtifactGroupEntity`]->(a2:`ArtifactGroup`) | [ r_r2, a2 ] ] ]"; + + // String query = "MATCH (n:`RepositoryArtifactIdGroup`) WHERE n.`uuid` + // = { id } WITH n RETURN n,[ [ + // (n)-[r_r1:`RepositoryArtifactIdGroupEntity_ArtifactGroupEntity`]->(a1:`ArtifactGroup`) + // | [ r_r1, a1 ] ] ]"; + + System.out.println(normalizeMatchByIdWithRelationResult(query, "123")); + } + +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/GremlinGraphDriver.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/GremlinGraphDriver.java new file mode 100644 index 0000000..67292a2 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/GremlinGraphDriver.java @@ -0,0 +1,79 @@ +package org.opencypher.gremlin.neo4j.ogm; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.neo4j.ogm.config.Configuration; +import org.neo4j.ogm.driver.AbstractConfigurableDriver; +import org.neo4j.ogm.request.Request; +import org.neo4j.ogm.transaction.Transaction; +import org.neo4j.ogm.transaction.Transaction.Type; +import org.neo4j.ogm.transaction.TransactionManager; +import org.opencypher.gremlin.neo4j.driver.CypherGremlinStatementRunner; +import org.opencypher.gremlin.neo4j.ogm.request.GremlinRequest; +import org.opencypher.gremlin.neo4j.ogm.transaction.GremlinTransaction; + +/** + * @author sbespalov + */ +public class GremlinGraphDriver extends AbstractConfigurableDriver +{ + + private org.apache.tinkerpop.gremlin.structure.Transaction transactionContext; + + public GremlinGraphDriver(org.apache.tinkerpop.gremlin.structure.Transaction transactionContext) + { + this.transactionContext = transactionContext; + } + + @Override + public void configure(Configuration config) + { + + } + + @Override + public Function, Transaction>> getTransactionFactorySupplier() + { + return transactionManager -> (type, + bookmarks) -> { + return createTransaction(transactionManager, type); + }; + + } + + private Transaction createTransaction(TransactionManager transactionManager, + Type type) + { + // TODO: Type.READ_ONLY + Graph tx = transactionContext.createThreadedTx(); + + return new GremlinTransaction(transactionManager, tx, type); + } + + @Override + public void close() + { + transactionContext.close(); + } + + @Override + public Request request(Transaction transaction) + { + return new GremlinRequest(new CypherGremlinStatementRunner((GremlinTransaction) transaction)); + } + + @Override + public Configuration getConfiguration() + { + return null; + } + + @Override + protected String getTypeSystemName() + { + return GremlinGraphDriver.class.getName() + ".types"; + } + +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/request/GremlinRequest.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/request/GremlinRequest.java new file mode 100644 index 0000000..acf9adf --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/request/GremlinRequest.java @@ -0,0 +1,262 @@ +package org.opencypher.gremlin.neo4j.ogm.request; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +import org.apache.commons.lang3.tuple.Pair; +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.driver.v1.StatementRunner; +import org.neo4j.driver.v1.exceptions.ClientException; +import org.neo4j.ogm.exception.CypherException; +import org.neo4j.ogm.model.GraphModel; +import org.neo4j.ogm.model.GraphRowListModel; +import org.neo4j.ogm.model.RestModel; +import org.neo4j.ogm.model.RowModel; +import org.neo4j.ogm.request.DefaultRequest; +import org.neo4j.ogm.request.GraphModelRequest; +import org.neo4j.ogm.request.GraphRowListModelRequest; +import org.neo4j.ogm.request.Request; +import org.neo4j.ogm.request.RestModelRequest; +import org.neo4j.ogm.request.RowModelRequest; +import org.neo4j.ogm.request.Statement; +import org.neo4j.ogm.response.EmptyResponse; +import org.neo4j.ogm.response.Response; +import org.opencypher.gremlin.neo4j.driver.Neo4jDriverEntityAdapter; +import org.opencypher.gremlin.neo4j.ogm.CypherQueryUtils; +import org.opencypher.gremlin.neo4j.ogm.response.GremlinGraphRowModelResponse; +import org.opencypher.gremlin.neo4j.ogm.response.GremlinModelResponse; +import org.opencypher.gremlin.neo4j.ogm.response.GremlinRestModelResponse; +import org.opencypher.gremlin.neo4j.ogm.response.GremlinRowModelResponse; +import org.opencypher.gremlin.translation.CypherAst; +import org.opencypher.gremlin.translation.groovy.GroovyPredicate; +import org.opencypher.gremlin.translation.translator.Translator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GremlinRequest implements Request +{ + + private static final Logger logger = LoggerFactory.getLogger(GremlinRequest.class); + + private final StatementRunner statementRunner; + private final Neo4jDriverEntityAdapter entityAdapter = new Neo4jDriverEntityAdapter(); + + public GremlinRequest(StatementRunner statementRunner) + { + this.statementRunner = statementRunner; + } + + @Override + public Response execute(GraphModelRequest query) + { + if (query.getStatement().length() == 0) + { + return new EmptyResponse<>(); + } + return new GremlinModelResponse(executeRequest(query), entityAdapter); + } + + @Override + public Response execute(RowModelRequest query) + { + if (query.getStatement().length() == 0) + { + return new EmptyResponse(); + } + return new GremlinRowModelResponse(executeRequest(query), entityAdapter); + } + + @Override + public Response execute(DefaultRequest query) + { + final List rowModels = new ArrayList<>(); + String[] columns = null; + for (Statement statement : query.getStatements()) + { + + StatementResult result = executeRequest(statement); + + if (columns == null) + { + try + { + List columnSet = result.keys(); + columns = columnSet.toArray(new String[columnSet.size()]); + } + catch (ClientException e) + { + throw new CypherException(e.code(), e.getMessage(), e); + } + } + try (GremlinRowModelResponse rowModelResponse = new GremlinRowModelResponse(result, entityAdapter)) + { + RowModel model; + while ((model = rowModelResponse.next()) != null) + { + rowModels.add(model); + } + result.consume(); + } + } + + return new MultiStatementBasedResponse(columns, rowModels); + } + + @Override + public Response execute(GraphRowListModelRequest query) + { + if (query.getStatement().length() == 0) + { + return new EmptyResponse(); + } + return new GremlinGraphRowModelResponse(executeRequest(query), entityAdapter); + } + + @Override + public Response execute(RestModelRequest query) + { + if (query.getStatement().length() == 0) + { + return new EmptyResponse(); + } + return new GremlinRestModelResponse(executeRequest(query), entityAdapter); + } + + private org.neo4j.driver.v1.StatementResult executeRequest(Statement query) + { + Map parameterMap = query.getParameters(); + String cypherStatement = query.getStatement(); + logger.debug("Cypher: {} with params {}", cypherStatement, parameterMap); + + Pair> cypherWithParams = Pair.of(cypherStatement, parameterMap); + cypherStatement = Optional.of(cypherWithParams) + .map(this::inlineParameters) + .map(this::normalizeMergeByIdWithParams) + .map(this::normalizeMatchByIdWithRelationResult) + .map(Pair::getLeft) + .get(); + + logger.trace("Cypher(normalized): {}", cypherStatement); + if (logger.isTraceEnabled()) + { + CypherAst ast = CypherAst.parse(cypherStatement, parameterMap); + Translator translator = Translator.builder() + .gremlinGroovy() + .enableCypherExtensions() + .build(); + logger.trace("Gremlin: {}", ast.buildTranslation(translator)); + } + + Class pageableClass; + try + { + pageableClass = Class.forName("org.springframework.data.domain.Pageable"); + } + catch (ClassNotFoundException e) + { + pageableClass = null; + } + if (pageableClass != null) + { + for (Iterator> iterator = parameterMap.entrySet().iterator(); iterator.hasNext();) + { + Entry next = iterator.next(); + if (pageableClass.isInstance(next.getValue())) + { + iterator.remove(); + } + } + } + + return statementRunner.run(cypherStatement, parameterMap); + } + + protected Pair> normalizeMatchByIdWithRelationResult(Pair> cyphterWithParams) + { + return Optional.of(cyphterWithParams) + .map(p -> p.getRight()) + .map(p -> p.get("id")) + .map(id -> id.toString()) + .map(id -> CypherQueryUtils.normalizeMatchByIdWithRelationResult(cyphterWithParams.getLeft(), + id)) + .map(s -> Pair.of(s, cyphterWithParams.getRight())) + .orElse(cyphterWithParams); + } + + protected Pair> normalizeMergeByIdWithParams(Pair> cyphterWithParams) + { + return Optional.of(cyphterWithParams) + .map(p -> p.getRight()) + .map(p -> p.get("rows")) + .filter(r -> r instanceof Collection) + .map(r -> (Collection) r) + .map(r -> r.iterator().next()) + .map(r -> (Map) r) + .map(r -> r.get("props")) + .filter(p -> p instanceof Map) + .map(p -> (Map) p) + .map(p -> CypherQueryUtils.normalizeMergeByIdWithParams(cyphterWithParams.getLeft(), p)) + .map(s -> Pair.of(s, cyphterWithParams.getRight())) + .orElse(cyphterWithParams); + } + + protected Pair> inlineParameters(Pair> cyphterWithParams) + { + return Optional.of(cyphterWithParams.getRight()) + .filter(p -> !p.isEmpty()) + .map(p -> CypherQueryUtils.inlineParameters(cyphterWithParams.getLeft(), p)) + .map(s -> Pair.of(s, cyphterWithParams.getRight())) + .orElse(cyphterWithParams); + } + + private static class MultiStatementBasedResponse implements Response + { + // This implementation is not good, but it preserved the current + // behaviour while fixing another bug. + // While the statements executed in + // org.neo4j.ogm.drivers.bolt.request.BoltRequest.execute(org.neo4j.ogm.request.DefaultRequest) + // might return different columns, only the ones of the first result are + // used. :( + private final String[] columns; + private final List rowModels; + + private int currentRow = 0; + + MultiStatementBasedResponse(String[] columns, + List rowModels) + { + this.columns = columns; + this.rowModels = rowModels; + } + + @Override + public RowModel next() + { + if (currentRow < rowModels.size()) + { + return rowModels.get(currentRow++); + } + return null; + } + + @Override + public void close() + { + } + + @Override + public String[] columns() + { + return this.columns; + } + } + +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinGraphRowModelResponse.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinGraphRowModelResponse.java new file mode 100644 index 0000000..df447d1 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinGraphRowModelResponse.java @@ -0,0 +1,42 @@ +package org.opencypher.gremlin.neo4j.ogm.response; + +import java.util.Arrays; + +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.ogm.model.GraphRowListModel; +import org.neo4j.ogm.response.model.DefaultGraphRowListModel; +import org.neo4j.ogm.result.adapter.GraphRowModelAdapter; +import org.opencypher.gremlin.neo4j.driver.Neo4jDriverEntityAdapter; + +public class GremlinGraphRowModelResponse extends GremlinResponse +{ + + private final GraphRowModelAdapter adapter; + + public GremlinGraphRowModelResponse(StatementResult result, + Neo4jDriverEntityAdapter entityAdapter) + { + + super(result); + + this.adapter = new GraphRowModelAdapter(new GremlinModelResponse.GremlinGraphModelAdapter(entityAdapter)); + this.adapter.setColumns(Arrays.asList(columns())); + } + + @Override + public GraphRowListModel fetchNext() + { + if (result.hasNext()) + { + DefaultGraphRowListModel model = new DefaultGraphRowListModel(); + model.add(adapter.adapt(result.next().asMap())); + + while (result.hasNext()) + { + model.add(adapter.adapt(result.next().asMap())); + } + return model; + } + return null; + } +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinModelResponse.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinModelResponse.java new file mode 100644 index 0000000..18d9ab0 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinModelResponse.java @@ -0,0 +1,118 @@ +package org.opencypher.gremlin.neo4j.ogm.response; + +import java.util.List; +import java.util.Map; + +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.ogm.model.GraphModel; +import org.neo4j.ogm.result.adapter.GraphModelAdapter; +import org.opencypher.gremlin.neo4j.driver.Neo4jDriverEntityAdapter; + +public class GremlinModelResponse extends GremlinResponse +{ + + private final GremlinGraphModelAdapter adapter; + + public GremlinModelResponse(StatementResult result, + Neo4jDriverEntityAdapter entityAdapter) + { + + super(result); + + this.adapter = new GremlinGraphModelAdapter(entityAdapter); + } + + @Override + public GraphModel fetchNext() + { + if (result.hasNext()) + { + return adapter.adapt(result.next().asMap()); + } + return null; + } + + static class GremlinGraphModelAdapter extends GraphModelAdapter + { + + private final Neo4jDriverEntityAdapter entityAdapter; + + GremlinGraphModelAdapter(Neo4jDriverEntityAdapter entityAdapter) + { + this.entityAdapter = entityAdapter; + } + + @Override + public boolean isPath(Object value) + { + return entityAdapter.isPath(value); + } + + @Override + public boolean isNode(Object value) + { + return entityAdapter.isNode(value); + } + + @Override + public boolean isRelationship(Object value) + { + return entityAdapter.isRelationship(value); + } + + @Override + public long nodeId(Object node) + { + return entityAdapter.nodeId(node); + } + + @Override + public List labels(Object entity) + { + return entityAdapter.labels(entity); + } + + @Override + public long relationshipId(Object relationship) + { + return entityAdapter.relationshipId(relationship); + } + + @Override + public String relationshipType(Object entity) + { + return entityAdapter.relationshipType(entity); + } + + @Override + public Long startNodeId(Object relationship) + { + return entityAdapter.startNodeId(relationship); + } + + @Override + public Long endNodeId(Object relationship) + { + return entityAdapter.endNodeId(relationship); + } + + @Override + public Map properties(Object container) + { + return entityAdapter.properties(container); + } + + @Override + public List nodesInPath(Object pathValue) + { + return entityAdapter.nodesInPath(pathValue); + } + + @Override + public List relsInPath(Object pathValue) + { + return entityAdapter.relsInPath(pathValue); + } + + } +} \ No newline at end of file diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinResponse.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinResponse.java new file mode 100644 index 0000000..ec5b380 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinResponse.java @@ -0,0 +1,47 @@ +package org.opencypher.gremlin.neo4j.ogm.response; + +import java.util.Set; + +import org.neo4j.driver.v1.Record; +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.ogm.response.Response; + +public abstract class GremlinResponse implements Response +{ + + final StatementResult result; + + GremlinResponse(StatementResult result) + { + this.result = result; + } + + @Override + public T next() + { + return fetchNext(); + } + + protected abstract T fetchNext(); + + @Override + public void close() + { + result.consume(); + } + + @Override + public String[] columns() + { + if (result.hasNext()) + { + Record record = result.peek(); + if (record != null) + { + Set columns = result.peek().asMap().keySet(); + return columns.toArray(new String[columns.size()]); + } + } + return new String[0]; + } +} \ No newline at end of file diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRestModelResponse.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRestModelResponse.java new file mode 100644 index 0000000..d9b792d --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRestModelResponse.java @@ -0,0 +1,155 @@ +package org.opencypher.gremlin.neo4j.ogm.response; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.neo4j.driver.v1.Record; +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.ogm.config.ObjectMapperFactory; +import org.neo4j.ogm.model.QueryStatistics; +import org.neo4j.ogm.model.RestModel; +import org.neo4j.ogm.response.model.DefaultRestModel; +import org.neo4j.ogm.response.model.QueryStatisticsModel; +import org.neo4j.ogm.result.adapter.RestModelAdapter; +import org.neo4j.ogm.result.adapter.ResultAdapter; +import org.opencypher.gremlin.neo4j.driver.Neo4jDriverEntityAdapter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class GremlinRestModelResponse extends GremlinResponse +{ + + private final RestModelAdapter restModelAdapter; + private final QueryStatisticsModel statisticsModel; + private final Iterator resultProjection; + + public GremlinRestModelResponse(StatementResult result, + Neo4jDriverEntityAdapter entityAdapter) + { + + super(result); + + this.restModelAdapter = new GremlinRestModelAdapter(entityAdapter); + this.resultProjection = result.list().iterator(); + this.statisticsModel = new StatisticsModelAdapter().adapt(result); + } + + @Override + public RestModel fetchNext() + { + return DefaultRestModel.basedOn(buildModel()) + .orElse(null); + } + + private Map buildModel() + { + Map row = new LinkedHashMap<>(); + if (resultProjection.hasNext()) + { + row = restModelAdapter.adapt(resultProjection.next().asMap()); + } + + return row; + } + + @Override + public Optional getStatistics() + { + return Optional.of(statisticsModel); + } + + static class GremlinRestModelAdapter extends RestModelAdapter + { + + private final Neo4jDriverEntityAdapter entityAdapter; + + public GremlinRestModelAdapter(Neo4jDriverEntityAdapter entityAdapter) + { + this.entityAdapter = entityAdapter; + } + + @Override + public boolean isNode(Object value) + { + return entityAdapter.isNode(value); + } + + @Override + public boolean isRelationship(Object value) + { + return entityAdapter.isRelationship(value); + } + + @Override + public long nodeId(Object node) + { + return entityAdapter.nodeId(node); + } + + @Override + public List labels(Object value) + { + return entityAdapter.labels(value); + } + + @Override + public long relationshipId(Object relationship) + { + return entityAdapter.relationshipId(relationship); + } + + @Override + public String relationshipType(Object relationship) + { + return entityAdapter.relationshipType(relationship); + } + + @Override + public Long startNodeId(Object relationship) + { + return entityAdapter.startNodeId(relationship); + } + + @Override + public Long endNodeId(Object relationship) + { + return entityAdapter.endNodeId(relationship); + } + + @Override + public Map properties(Object container) + { + return entityAdapter.properties(container); + } + } + + static class StatisticsModelAdapter + implements ResultAdapter + { + + protected static final ObjectMapper mapper = ObjectMapperFactory.objectMapper(); + + @Override + public QueryStatisticsModel adapt(org.neo4j.driver.v1.StatementResult result) + { + QueryStatisticsModel queryStatisticsModel = new QueryStatisticsModel(); + org.neo4j.driver.v1.summary.SummaryCounters stats = result.consume().counters(); + queryStatisticsModel.setContains_updates(stats.containsUpdates()); + queryStatisticsModel.setNodes_created(stats.nodesCreated()); + queryStatisticsModel.setNodes_deleted(stats.nodesDeleted()); + queryStatisticsModel.setProperties_set(stats.propertiesSet()); + queryStatisticsModel.setRelationships_created(stats.relationshipsCreated()); + queryStatisticsModel.setRelationship_deleted(stats.relationshipsDeleted()); + queryStatisticsModel.setLabels_added(stats.labelsAdded()); + queryStatisticsModel.setLabels_removed(stats.labelsRemoved()); + queryStatisticsModel.setIndexes_added(stats.indexesAdded()); + queryStatisticsModel.setIndexes_removed(stats.indexesRemoved()); + queryStatisticsModel.setConstraints_added(stats.constraintsAdded()); + queryStatisticsModel.setConstraints_removed(stats.constraintsRemoved()); + return queryStatisticsModel; + } + } +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRowModelResponse.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRowModelResponse.java new file mode 100644 index 0000000..dc6b7e0 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/response/GremlinRowModelResponse.java @@ -0,0 +1,65 @@ +package org.opencypher.gremlin.neo4j.ogm.response; + +import java.util.Arrays; + +import org.neo4j.driver.v1.StatementResult; +import org.neo4j.ogm.model.RowModel; +import org.neo4j.ogm.result.adapter.RowModelAdapter; +import org.opencypher.gremlin.neo4j.driver.Neo4jDriverEntityAdapter; + +public class GremlinRowModelResponse extends GremlinResponse +{ + + private final GremlinRowModelAdapter adapter; + + public GremlinRowModelResponse(StatementResult result, + Neo4jDriverEntityAdapter entityAdapter) + { + + super(result); + + this.adapter = new GremlinRowModelAdapter(entityAdapter); + this.adapter.setColumns(Arrays.asList(columns())); + } + + @Override + public RowModel fetchNext() + { + if (result.hasNext()) + { + return adapter.adapt(result.next().asMap()); + } + return null; + } + + class GremlinRowModelAdapter extends RowModelAdapter + + { + + private final Neo4jDriverEntityAdapter entityAdapter; + + public GremlinRowModelAdapter(Neo4jDriverEntityAdapter entityAdapter) + { + this.entityAdapter = entityAdapter; + } + + @Override + public boolean isPath(Object value) + { + return entityAdapter.isPath(value); + } + + @Override + public boolean isNode(Object value) + { + return entityAdapter.isNode(value); + } + + @Override + public boolean isRelationship(Object value) + { + return entityAdapter.isRelationship(value); + } + + } +} diff --git a/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/transaction/GremlinTransaction.java b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/transaction/GremlinTransaction.java new file mode 100644 index 0000000..23ad431 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/opencypher/gremlin/neo4j/ogm/transaction/GremlinTransaction.java @@ -0,0 +1,120 @@ +package org.opencypher.gremlin.neo4j.ogm.transaction; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.neo4j.ogm.exception.TransactionException; +import org.neo4j.ogm.transaction.AbstractTransaction; +import org.neo4j.ogm.transaction.TransactionManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GremlinTransaction extends AbstractTransaction +{ + + private final Logger logger = LoggerFactory.getLogger(GremlinTransaction.class); + + private final Graph nativeTransaction; + + public GremlinTransaction(TransactionManager transactionManager, + Graph nativeTransaction, + Type type) + { + super(transactionManager); + this.type = type; + this.nativeTransaction = nativeTransaction; + } + + public Graph getNativeTransaction() + { + return nativeTransaction; + } + + @Override + public void rollback() + { + try + { + doRollback(); + } + catch (Exception e) + { + throw new TransactionException(e.getLocalizedMessage(), e); + } + finally + { + super.rollback(); + } + } + + protected void doRollback() throws Exception + { + if (!transactionManager.canRollback()) + { + logger.debug("Skip rolback."); + + return; + } + + logger.debug("Rolling back native transaction: {}", nativeTransaction); + + if (!nativeTransaction.tx().isOpen()) + { + logger.warn("Transaction is already closed"); + + return; + } + + try + { + nativeTransaction.tx().rollback(); + nativeTransaction.tx().close(); + } + finally + { + nativeTransaction.close(); + } + } + + @Override + public void commit() + { + try + { + doCommit(); + } + catch (Exception e) + { + super.rollback(); + throw new TransactionException(e.getLocalizedMessage(), e); + } + + super.commit(); + } + + protected void doCommit() throws Exception + { + if (!transactionManager.canCommit()) + { + logger.debug("Skip commit."); + + return; + } + + if (!nativeTransaction.tx().isOpen()) + { + throw new IllegalStateException("Transaction is already closed"); + } + + logger.debug("Committing native transaction: {}", nativeTransaction); + + try + { + nativeTransaction.tx().commit(); + nativeTransaction.tx().close(); + } + finally + { + nativeTransaction.close(); + } + } + +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedConfiguration.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedConfiguration.java new file mode 100644 index 0000000..7dc31bd --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedConfiguration.java @@ -0,0 +1,11 @@ +package org.strongbox.db.server; + +public interface CassandraEmbeddedConfiguration +{ + + Integer getPort(); + + String getStorageFolder(); + + String getCassandraConfigLoaderClassName(); +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedProperties.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedProperties.java new file mode 100644 index 0000000..bd82f4b --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/CassandraEmbeddedProperties.java @@ -0,0 +1,122 @@ +package org.strongbox.db.server; + +import java.util.Collections; +import java.util.Objects; + +import org.apache.cassandra.config.Config; +import org.apache.cassandra.config.Config.CommitLogSync; +import org.apache.cassandra.config.Config.DiskFailurePolicy; +import org.apache.cassandra.config.ConfigurationLoader; +import org.apache.cassandra.config.ParameterizedClass; +import org.apache.cassandra.exceptions.ConfigurationException; + +public class CassandraEmbeddedProperties implements CassandraEmbeddedConfiguration +{ + + private volatile static CassandraEmbeddedProperties instance; + + public static synchronized CassandraEmbeddedProperties getInstance(String storageFolder, + int port) + { + if (instance != null) + { + return instance; + } + return instance = new CassandraEmbeddedProperties(storageFolder, port); + } + + private final Config config; + + private CassandraEmbeddedProperties(String storageFolder, + int port) + { + Objects.isNull(instance); + + Config configLocal = new Config(); + configLocal.cluster_name = "Test Cluster"; + configLocal.hinted_handoff_enabled = true; + configLocal.max_hint_window_in_ms = 10800000; // 3 hours + configLocal.hinted_handoff_throttle_in_kb = 1024; + configLocal.max_hints_delivery_threads = 2; + configLocal.hints_directory = String.format("%s/cassandra/hints", storageFolder); + configLocal.authenticator = "PasswordAuthenticator"; + configLocal.authorizer = "AllowAllAuthorizer"; + configLocal.permissions_validity_in_ms = 2000; + configLocal.partitioner = "org.apache.cassandra.dht.Murmur3Partitioner"; + configLocal.data_file_directories = new String[] { String.format("%s/cassandra/data", storageFolder) }; + configLocal.commitlog_directory = String.format("%s/cassandra/commitlog", storageFolder); + configLocal.cdc_raw_directory = String.format("%s/cassandra/cdc", storageFolder); + configLocal.disk_failure_policy = DiskFailurePolicy.stop; + configLocal.key_cache_save_period = 14400; + configLocal.row_cache_size_in_mb = 0; + configLocal.row_cache_save_period = 0; + configLocal.saved_caches_directory = String.format("%s/cassandra/saved_caches", storageFolder); + configLocal.commitlog_sync = CommitLogSync.periodic; + configLocal.commitlog_sync_period_in_ms = 10000; + configLocal.commitlog_segment_size_in_mb = 8; + configLocal.max_mutation_size_in_kb = 4096; + configLocal.seed_provider = new ParameterizedClass("org.apache.cassandra.locator.SimpleSeedProvider", + Collections.singletonMap("seeds", "127.0.0.1")); + configLocal.concurrent_reads = 32; + configLocal.concurrent_writes = 32; + configLocal.trickle_fsync = false; + configLocal.trickle_fsync_interval_in_kb = 10240; + configLocal.storage_port = 7010; + configLocal.ssl_storage_port = 7011; + configLocal.listen_address = "127.0.0.1"; + + configLocal.start_native_transport = true; + configLocal.native_transport_port = port; + + configLocal.start_rpc = false; + configLocal.incremental_backups = false; + configLocal.snapshot_before_compaction = false; + configLocal.auto_snapshot = false; + configLocal.column_index_size_in_kb = 64; + configLocal.compaction_throughput_mb_per_sec = 16; + configLocal.read_request_timeout_in_ms = 5000; + configLocal.range_request_timeout_in_ms = 10000; + configLocal.write_request_timeout_in_ms = 2000; + configLocal.cas_contention_timeout_in_ms = 1000; + configLocal.truncate_request_timeout_in_ms = 60000; + configLocal.request_timeout_in_ms = 10000; + configLocal.cross_node_timeout = false; + configLocal.endpoint_snitch = "SimpleSnitch"; + configLocal.dynamic_snitch_update_interval_in_ms = 100; + configLocal.dynamic_snitch_reset_interval_in_ms = 600000; + configLocal.dynamic_snitch_badness_threshold = 0.1; + configLocal.request_scheduler = "org.apache.cassandra.scheduler.NoScheduler"; + configLocal.index_interval = 128; + + this.config = configLocal; + } + + public Integer getPort() + { + return config.native_transport_port; + } + + @Override + public String getCassandraConfigLoaderClassName() + { + return "org.strongbox.db.server.CassandraEmbeddedProperties$CassandraEmbeddedPropertiesLoader"; + } + + public String getStorageFolder() + { + return config.data_file_directories[0].replace("/cassandra/data", ""); + } + + public static class CassandraEmbeddedPropertiesLoader implements ConfigurationLoader + { + @Override + public Config loadConfig() + throws ConfigurationException + { + Objects.nonNull(instance); + + return instance.config; + } + + } +} \ No newline at end of file diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedDbServer.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedDbServer.java new file mode 100644 index 0000000..a720434 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedDbServer.java @@ -0,0 +1,10 @@ +package org.strongbox.db.server; + +public interface EmbeddedDbServer +{ + + void start() throws Exception; + + void stop() throws Exception; + +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedJanusGraphWithCassandraServer.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedJanusGraphWithCassandraServer.java new file mode 100644 index 0000000..be51cff --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedJanusGraphWithCassandraServer.java @@ -0,0 +1,220 @@ +package org.strongbox.db.server; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.concurrent.ExecutionException; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.apache.cassandra.service.CassandraDaemon; +import org.apache.cassandra.service.StorageService; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphFactory; +import org.janusgraph.graphdb.database.StandardJanusGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Embedded JanusGraph+Cassandra server + * + * @author sbespalov + */ +public class EmbeddedJanusGraphWithCassandraServer + implements EmbeddedDbServer +{ + + private static final Logger logger = LoggerFactory.getLogger(EmbeddedJanusGraphWithCassandraServer.class); + + private final CassandraEmbeddedConfiguration cassandraEmbeddedProperties; + private final JanusGraphConfiguration janusGraphProperties; + private volatile JanusGraph janusGraph; + private volatile CassandraDaemon cassandraDaemon; + + public EmbeddedJanusGraphWithCassandraServer(CassandraEmbeddedConfiguration cassandraEmbeddedProperties, + JanusGraphConfiguration janusGraphProperties) + { + this.janusGraphProperties = janusGraphProperties; + this.cassandraEmbeddedProperties = cassandraEmbeddedProperties; + } + + public JanusGraph getJanusGraph() + { + return janusGraph; + } + + public CassandraDaemon getCassandraDaemon() + { + return cassandraDaemon; + } + + @PostConstruct + public synchronized void start() + throws Exception + { + try + { + provideCassandraInstance(); + provideJanusGraphInstanceWithRetry(); + } + catch (Exception e) + { + stop(); + throw new RuntimeException("Unable to start the embedded DB server!", e); + } + } + + private CassandraDaemon provideCassandraInstance() + { + if (cassandraDaemon != null) + { + return cassandraDaemon; + } + + System.setProperty("cassandra.config.loader", + cassandraEmbeddedProperties.getCassandraConfigLoaderClassName()); + + System.setProperty("cassandra-foreground", "true"); + System.setProperty("cassandra.native.epoll.enabled", "false"); + System.setProperty("cassandra.unsafesystem", "true"); + + CassandraDaemon cassandraDaemonLocal = new CassandraDaemon(true); + cassandraDaemonLocal.activate(); + + StorageService.instance.removeShutdownHook(); + + return this.cassandraDaemon = cassandraDaemonLocal; + } + + private JanusGraph provideJanusGraphInstanceWithRetry() + throws Exception + { + for (int i = 0; i < 20; i++) + { + try + { + return provideJanusGraphInstance(); + } + catch (Exception e) + { + Thread.sleep(500); + logger.info(String.format("Retry JanusGraph instance initialization with [%s] attempt...", i)); + } + } + + return provideJanusGraphInstance(); + } + + private JanusGraph provideJanusGraphInstance() + throws Exception + { + if (janusGraph != null) + { + return janusGraph; + } + + JanusGraph janusGraphLocal = JanusGraphFactory.build() + .set("storage.backend", "cql") + .set("storage.hostname", janusGraphProperties.getStorageHost()) + .set("storage.port", janusGraphProperties.getStoragePort()) + .set("storage.username", + janusGraphProperties.getStorageUsername()) + .set("storage.password", + janusGraphProperties.getStoragePassword()) + .set("storage.cql.keyspace", "strongbox") + .set("storage.cql.only-use-local-consistency-for-system-operations", + true) + .set("tx.log-tx", true) + .set("schema.default", "none") + .set("schema.constraints", true) + .open(); + + try + { + Method removeHookMethod = StandardJanusGraph.class.getDeclaredMethod("removeHook"); + removeHookMethod.setAccessible(true); + removeHookMethod.invoke(janusGraphLocal); + } + catch (Exception e) + { + janusGraphLocal.close(); + throw e; + } + + return this.janusGraph = janusGraphLocal; + } + + @PreDestroy + @Override + public synchronized void stop() + throws Exception + { + try + { + closeJanusGraph(); + } + catch (Exception e) + { + logger.error("Failed to close JanusGraph", e); + } + + try + { + closeCassandra(); + } + catch (Exception e) + { + logger.error("Failed to close Cassandra", e); + } + + logger.info("All embedded DB services stopped!"); + } + + private void closeCassandra() + throws IOException, + InterruptedException, + ExecutionException + { + if (cassandraDaemon == null) + { + logger.info("Skip closing cassandra daemon.."); + return; + } + logger.info("Shutting down cassandra daemon.."); + try + { + StorageService.instance.drain(); + } + catch (Exception e) + { + logger.error("Failed to drain cassandra service.", e); + } + try + { + cassandraDaemon.deactivate(); + } + finally + { + cassandraDaemon = null; + } + } + + private void closeJanusGraph() + { + if (janusGraph == null) + { + logger.info("Skip closing JanusGraph.."); + return; + } + logger.info("Shutting JanusGraph instance.."); + try + { + janusGraph.close(); + } + finally + { + janusGraph = null; + } + } + +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedOrientDbServer.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedOrientDbServer.java deleted file mode 100644 index 8247a1c..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/EmbeddedOrientDbServer.java +++ /dev/null @@ -1,283 +0,0 @@ -package org.strongbox.db.server; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.JarURLConnection; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.orientechnologies.orient.core.config.OGlobalConfiguration; -import com.orientechnologies.orient.graph.server.command.OServerCommandGetGephi; -import com.orientechnologies.orient.server.OServer; -import com.orientechnologies.orient.server.OServerMain; -import com.orientechnologies.orient.server.config.OServerCommandConfiguration; -import com.orientechnologies.orient.server.config.OServerConfiguration; -import com.orientechnologies.orient.server.config.OServerEntryConfiguration; -import com.orientechnologies.orient.server.config.OServerNetworkConfiguration; -import com.orientechnologies.orient.server.config.OServerNetworkListenerConfiguration; -import com.orientechnologies.orient.server.config.OServerNetworkProtocolConfiguration; -import com.orientechnologies.orient.server.config.OServerParameterConfiguration; -import com.orientechnologies.orient.server.config.OServerUserConfiguration; -import com.orientechnologies.orient.server.network.protocol.http.command.get.OServerCommandGetStaticContent; - -/** - * An embedded configuration of OrientDb server. - * - * @author Alex Oreshkevich - */ -public class EmbeddedOrientDbServer - implements OrientDbServer -{ - - private static final Logger logger = LoggerFactory.getLogger(EmbeddedOrientDbServer.class); - - public static final String ORIENTDB_STUDIO_VERSION = "2.2.0"; - - private OServer server; - private OServerConfiguration serverConfiguration; - - private OrientDbStudioConfiguration studioProperties; - private OrientDbServerConfiguration serverProperties; - - public EmbeddedOrientDbServer(OrientDbStudioConfiguration studioProperties, - OrientDbServerConfiguration serverProperties) - { - super(); - - this.studioProperties = studioProperties; - this.serverProperties = serverProperties; - } - - @PostConstruct - public void start() - { - try - { - init(); - prepareStudio(); - activate(); - } - catch (Exception e) - { - throw new RuntimeException("Unable to start the embedded OrientDb server!", e); - } - } - - public JarFile getStudioClasspathLocation() - throws IOException - { - URL systemResource = OServer.class.getResource(String.format("/META-INF/resources/webjars/orientdb-studio/%s", - ORIENTDB_STUDIO_VERSION)); - JarURLConnection connection = (JarURLConnection) systemResource.openConnection(); - - return connection.getJarFile(); - } - - private void prepareStudio() - throws IOException - { - if (!studioProperties.isEnabled()) - { - logger.info("OrientDB Studio disabled with, skip initialization."); - - return; - } - - OServerNetworkListenerConfiguration httpListener = new OServerNetworkListenerConfiguration(); - httpListener.ipAddress = studioProperties.getIpAddress(); - httpListener.portRange = String.valueOf(studioProperties.getPort()); - httpListener.protocol = "http"; - httpListener.socket = "default"; - - OServerCommandConfiguration httpCommandConfiguration1 = new OServerCommandConfiguration(); - httpCommandConfiguration1.implementation = OServerCommandGetStaticContent.class.getCanonicalName(); - httpCommandConfiguration1.pattern = "GET|www GET|studio/ GET| GET|*.htm GET|*.html GET|*.xml GET|*.jpeg GET|*.jpg GET|*.png GET|*.gif GET|*.js GET|*.css GET|*.swf GET|*.ico GET|*.txt GET|*.otf GET|*.pjs GET|*.svg GET|*.json GET|*.woff GET|*.ttf GET|*.svgz"; - httpCommandConfiguration1.stateful = false; - httpCommandConfiguration1.parameters = new OServerEntryConfiguration[] { new OServerEntryConfiguration( - "http.cache:*.htm *.html", - "Cache-Control: no-cache, no-store, max-age=0, must-revalidate\\r\\nPragma: no-cache"), - new OServerEntryConfiguration( - "http.cache:default", - "Cache-Control: max-age=120") }; - - OServerCommandConfiguration httpCommandConfiguration2 = new OServerCommandConfiguration(); - httpCommandConfiguration2.implementation = OServerCommandGetGephi.class.getCanonicalName(); - - httpListener.commands = new OServerCommandConfiguration[] { httpCommandConfiguration1, - httpCommandConfiguration2 }; - httpListener.parameters = new OServerParameterConfiguration[] { new OServerParameterConfiguration("utf-8", - "network.http.charset") }; - - serverConfiguration.network.listeners.add(httpListener); - - OServerNetworkProtocolConfiguration httpProtocol = new OServerNetworkProtocolConfiguration(); - httpProtocol.name = "http"; - httpProtocol.implementation = "com.orientechnologies.orient.server.network.protocol.http.ONetworkProtocolHttpDb"; - - serverConfiguration.network.protocols.add(httpProtocol); - - Path studioPath = resolvePath(studioProperties.getPath()).resolve("studio"); - if (Files.exists(studioPath)) - { - logger.info(String.format("OrientDB Studio is already available at [%s], skipping initialization. %n" + - "If you want to force the initialization of OrientDB Studio, please remove it's " + - "folder shown above.", - studioPath.toAbsolutePath().toString())); - return; - } - - logger.info(String.format("Initialized OrientDB Studio at [%s].", studioPath.toAbsolutePath().toString())); - - Files.createDirectories(studioPath); - - String root = String.format("META-INF/resources/webjars/orientdb-studio/%s/", ORIENTDB_STUDIO_VERSION); - - try (JarFile jar = getStudioClasspathLocation()) - { - Enumeration enumEntries = jar.entries(); - while (enumEntries.hasMoreElements()) - { - JarEntry file = enumEntries.nextElement(); - if (!file.getName().startsWith(root)) - { - continue; - } - - Path filePath = studioPath.resolve(file.getName().replace(root, "")); - if (file.isDirectory()) - { - Files.createDirectories(filePath); - continue; - } - - try (InputStream is = jar.getInputStream(file)) - { - try (FileOutputStream fos = new java.io.FileOutputStream(filePath.toFile())) - { - while (is.available() > 0) - { - fos.write(is.read()); - } - } - } - } - } - } - - protected Path resolvePath(String path) - { - return Paths.get(path).toAbsolutePath().normalize(); - } - - private void init() - throws Exception - { - logger.info(String.format("Initialized Embedded OrientDB server.")); - - // Don't touch below line. Don't move it down the code. It needs to be - // called before OServerMain.create() - // the size is in Kb - OGlobalConfiguration.NETWORK_BINARY_MAX_CONTENT_LENGTH.setValue(65536); - - - server = OServerMain.create(); - serverConfiguration = new OServerConfiguration(); - - // OServerHookConfiguration hookConfiguration = new - // OServerHookConfiguration(); - // serverConfiguration.hooks = Arrays.asList(new - // OServerHookConfiguration[] { hookConfiguration }); - // hookConfiguration.clazz = GenericEntityHook.class.getName(); - - OServerNetworkListenerConfiguration binaryListener = new OServerNetworkListenerConfiguration(); - binaryListener.ipAddress = serverProperties.getHost(); - binaryListener.portRange = serverProperties.getPort(); - binaryListener.protocol = "binary"; - binaryListener.socket = "default"; - - OServerNetworkProtocolConfiguration binaryProtocol = new OServerNetworkProtocolConfiguration(); - binaryProtocol.name = "binary"; - binaryProtocol.implementation = "com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary"; - - // prepare network configuration - OServerNetworkConfiguration networkConfiguration = new OServerNetworkConfiguration(); - - networkConfiguration.protocols = new LinkedList<>(); - networkConfiguration.protocols.add(binaryProtocol); - - networkConfiguration.listeners = new LinkedList<>(); - networkConfiguration.listeners.add(binaryListener); - - // add users (incl system-level root user) - List users = new LinkedList<>(); - users.add(buildUser(serverProperties.getUsername(), serverProperties.getPassword(), "*")); - - System.setProperty("ORIENTDB_ROOT_PASSWORD", serverProperties.getPassword()); - - String serverDatabasePath = resolvePath(serverProperties.getPath()).toString(); - // add other properties - List properties = new LinkedList<>(); - properties.add(buildProperty("server.database.path", serverDatabasePath)); - properties.add(buildProperty("plugin.dynamic", "false")); - properties.add(buildProperty("log.console.level", "info")); - properties.add(buildProperty("orientdb.www.path", serverDatabasePath)); - - serverConfiguration.network = networkConfiguration; - serverConfiguration.users = users.toArray(new OServerUserConfiguration[users.size()]); - serverConfiguration.properties = properties.toArray(new OServerEntryConfiguration[properties.size()]); - } - - private void activate() - throws Exception - { - if (!server.isActive()) - { - server.startup(serverConfiguration); - server.activate(); - } - } - - private OServerUserConfiguration buildUser(String name, - String password, - String resources) - { - OServerUserConfiguration user = new OServerUserConfiguration(); - user.name = name; - user.password = password; - user.resources = resources; - - return user; - } - - private OServerEntryConfiguration buildProperty(String name, - String value) - { - OServerEntryConfiguration property = new OServerEntryConfiguration(); - property.name = name; - property.value = value; - - return property; - } - - @PreDestroy - @Override - public void stop() - { - server.shutdown(); - } - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/InMemoryJanusGraphServer.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/InMemoryJanusGraphServer.java new file mode 100644 index 0000000..ccbda62 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/InMemoryJanusGraphServer.java @@ -0,0 +1,131 @@ +package org.strongbox.db.server; + +import java.lang.reflect.Method; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphFactory; +import org.janusgraph.graphdb.database.StandardJanusGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author sbespalov + */ +public class InMemoryJanusGraphServer implements EmbeddedDbServer +{ + private static final Logger logger = LoggerFactory.getLogger(EmbeddedJanusGraphWithCassandraServer.class); + + private final JanusGraphConfiguration janusGraphProperties; + private volatile JanusGraph janusGraph; + + public InMemoryJanusGraphServer(JanusGraphConfiguration janusGraphProperties) + { + this.janusGraphProperties = janusGraphProperties; + } + + public JanusGraph getJanusGraph() + { + return janusGraph; + } + + @PostConstruct + public synchronized void start() + throws Exception + { + try + { + provideJanusGraphInstanceWithRetry(); + } + catch (Exception e) + { + stop(); + throw new RuntimeException("Unable to start the embedded DB server!", e); + } + } + + private JanusGraph provideJanusGraphInstanceWithRetry() + throws Exception + { + for (int i = 0; i < 20; i++) + { + try + { + return provideJanusGraphInstance(); + } + catch (Exception e) + { + Thread.sleep(500); + logger.info(String.format("Retry JanusGraph instance initialization with [%s] attempt...", i)); + } + } + + return provideJanusGraphInstance(); + } + + private JanusGraph provideJanusGraphInstance() + throws Exception + { + if (janusGraph != null) + { + return janusGraph; + } + + JanusGraph janusGraphLocal = JanusGraphFactory.build() + .set("storage.backend", "inmemory") + .set("schema.default", "none") + .set("schema.constraints", true) + .open(); + + try + { + Method removeHookMethod = StandardJanusGraph.class.getDeclaredMethod("removeHook"); + removeHookMethod.setAccessible(true); + removeHookMethod.invoke(janusGraphLocal); + } + catch (Exception e) + { + janusGraphLocal.close(); + throw e; + } + + return this.janusGraph = janusGraphLocal; + } + + @PreDestroy + @Override + public synchronized void stop() + throws Exception + { + try + { + closeJanusGraph(); + } + catch (Exception e) + { + logger.error("Failed to close JanusGraph", e); + } + + logger.info("All embedded DB services stopped!"); + } + + private void closeJanusGraph() + { + if (janusGraph == null) + { + logger.info("Skip closing JanusGraph.."); + return; + } + logger.info("Shutting JanusGraph instance.."); + try + { + janusGraph.close(); + } + finally + { + janusGraph = null; + } + } +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphConfiguration.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphConfiguration.java new file mode 100644 index 0000000..6470dc3 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphConfiguration.java @@ -0,0 +1,16 @@ +package org.strongbox.db.server; + +public interface JanusGraphConfiguration +{ + + String getStorageUsername(); + + String getStoragePassword(); + + String getStorageHost(); + + Integer getStoragePort(); + + String getStorageRoot(); + +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphProperties.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphProperties.java new file mode 100644 index 0000000..698d13f --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/db/server/JanusGraphProperties.java @@ -0,0 +1,84 @@ +package org.strongbox.db.server; + +public class JanusGraphProperties implements JanusGraphConfiguration +{ + + private String storageUsername; + private String storagePassword; + private String storageHost; + private Integer storagePort; + private String storageRoot; + + public JanusGraphProperties() + { + } + + public JanusGraphProperties(String storageUsername, + String storagePassword, + String storageHost, + Integer storagePort, + String storageRoot) + { + this.storageUsername = storageUsername; + this.storagePassword = storagePassword; + this.storageHost = storageHost; + this.storagePort = storagePort; + this.storageRoot = storageRoot; + } + + @Override + public String getStorageUsername() + { + return storageUsername; + } + + @Override + public String getStoragePassword() + { + return storagePassword; + } + + @Override + public String getStorageHost() + { + return storageHost; + } + + @Override + public Integer getStoragePort() + { + return storagePort; + } + + @Override + public String getStorageRoot() + { + return storageRoot; + } + + public void setStorageUsername(String storageUsername) + { + this.storageUsername = storageUsername; + } + + public void setStoragePassword(String storagePassword) + { + this.storagePassword = storagePassword; + } + + public void setStorageHost(String storageHost) + { + this.storageHost = storageHost; + } + + public void setStoragePort(Integer storagePort) + { + this.storagePort = storagePort; + } + + public void setStorageRoot(String storageRoot) + { + this.storageRoot = storageRoot; + } + +} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServer.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServer.java deleted file mode 100644 index 276c9cf..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServer.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.strongbox.db.server; - -public interface OrientDbServer -{ - - void start(); - - void stop(); - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerConfiguration.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerConfiguration.java deleted file mode 100644 index b750679..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerConfiguration.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.strongbox.db.server; - -public interface OrientDbServerConfiguration -{ - - String getUsername(); - - String getPassword(); - - String getProtocol(); - - String getHost(); - - String getPort(); - - String getDatabase(); - - String getPath(); - - String getUrl(); - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerProperties.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerProperties.java deleted file mode 100644 index a717d0c..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbServerProperties.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.strongbox.db.server; - -public class OrientDbServerProperties implements OrientDbServerConfiguration -{ - - private String username; - private String password; - private String protocol; - private String host; - private String port; - private String database; - private String path; - - @Override - public String getUsername() - { - return username; - } - - public void setUsername(String username) - { - this.username = username; - } - - @Override - public String getPassword() - { - return password; - } - - public void setPassword(String password) - { - this.password = password; - } - - @Override - public String getProtocol() - { - return protocol; - } - - public void setProtocol(String protocol) - { - this.protocol = protocol; - } - - @Override - public String getHost() - { - return host; - } - - public void setHost(String host) - { - this.host = host; - } - - @Override - public String getPort() - { - return port; - } - - public void setPort(String port) - { - this.port = port; - } - - @Override - public String getDatabase() - { - return database; - } - - public void setDatabase(String database) - { - this.database = database; - } - - @Override - public String getPath() - { - return path; - } - - public void setPath(String path) - { - this.path = path; - } - - @Override - public String getUrl() - { - if ("memory".equals(protocol)) - { - return String.format("%s:%s", protocol, database); - } - - StringBuilder sb = new StringBuilder(); - sb.append(protocol); - if (host != null) - { - sb.append(":").append(host); - } - if (port != null) - { - sb.append(":").append(port); - } - sb.append("/").append(database); - - return sb.toString(); - } - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioConfiguration.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioConfiguration.java deleted file mode 100644 index 7444ab8..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioConfiguration.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.strongbox.db.server; - -public interface OrientDbStudioConfiguration -{ - - boolean isEnabled(); - - String getIpAddress(); - - int getPort(); - - String getPath(); - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioProperties.java b/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioProperties.java deleted file mode 100644 index d285304..0000000 --- a/strongbox-db-server/src/main/java/org/strongbox/db/server/OrientDbStudioProperties.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.strongbox.db.server; - -public class OrientDbStudioProperties implements OrientDbStudioConfiguration -{ - - private boolean enabled; - private String ipAddress; - private int port; - private String path; - - /* (non-Javadoc) - * @see org.strongbox.db.server.OrientDbStudioConfiguration#isEnabled() - */ - @Override - public boolean isEnabled() - { - return enabled; - } - - public void setEnabled(boolean studioEnabled) - { - this.enabled = studioEnabled; - } - - /* (non-Javadoc) - * @see org.strongbox.db.server.OrientDbStudioConfiguration#getIpAddress() - */ - @Override - public String getIpAddress() - { - return ipAddress; - } - - public void setIpAddress(String studioIpAddress) - { - this.ipAddress = studioIpAddress; - } - - /* (non-Javadoc) - * @see org.strongbox.db.server.OrientDbStudioConfiguration#getPort() - */ - @Override - public int getPort() - { - return port; - } - - public void setPort(int studioPort) - { - this.port = studioPort; - } - - /* (non-Javadoc) - * @see org.strongbox.db.server.OrientDbStudioConfiguration#getPath() - */ - @Override - public String getPath() - { - return path; - } - - public void setPath(String studioPath) - { - this.path = studioPath; - } - -} diff --git a/strongbox-db-server/src/main/java/org/strongbox/util/Commons.java b/strongbox-db-server/src/main/java/org/strongbox/util/Commons.java new file mode 100644 index 0000000..4bb8379 --- /dev/null +++ b/strongbox-db-server/src/main/java/org/strongbox/util/Commons.java @@ -0,0 +1,49 @@ +package org.strongbox.util; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +public class Commons +{ + + public static LocalDateTime toLocalDateTime(Date date) + { + if (date == null) + { + return null; + } + return date.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDateTime(); + } + + public static Date toDate(LocalDateTime date) + { + if (date == null) + { + return null; + } + return Date.from(date.atZone(ZoneId.systemDefault()).toInstant()); + } + + public static LocalDateTime toLocalDateTime(Long value) + { + if (value == null) + { + return null; + } + return Instant.ofEpochMilli(value).atZone(ZoneId.systemDefault()).toLocalDateTime(); + } + + public static Long toLong(LocalDateTime date) + { + if (date == null) + { + return null; + } + return date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + +} From 6547d5a7f6ae556eb95d042aec843347162176c7 Mon Sep 17 00:00:00 2001 From: Ankit Tomar Date: Sat, 25 Apr 2020 16:03:21 +0530 Subject: [PATCH 2/5] Extracted ArtifactArchiveListing to separate Vertex from Artifact --- pom.xml | 2 +- strongbox-db-import/pom.xml | 2 +- strongbox-db-schema/pom.xml | 2 +- .../carlspring/strongbox/db/schema/Edges.java | 6 +++++ .../strongbox/db/schema/Properties.java | 2 +- .../strongbox/db/schema/StrongboxSchema.java | 22 +++++++++---------- .../strongbox/db/schema/Vertices.java | 2 +- strongbox-db-server/pom.xml | 2 +- 8 files changed, 23 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 1f5755e..acca070 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ strongbox-db pom - 1.0-PR-19-SNAPSHOT + 1.0-PR-20-SNAPSHOT diff --git a/strongbox-db-import/pom.xml b/strongbox-db-import/pom.xml index a22cbc8..e557956 100644 --- a/strongbox-db-import/pom.xml +++ b/strongbox-db-import/pom.xml @@ -8,7 +8,7 @@ org.carlspring.strongbox strongbox-db - 1.0-PR-19-SNAPSHOT + 1.0-PR-20-SNAPSHOT ../pom.xml diff --git a/strongbox-db-schema/pom.xml b/strongbox-db-schema/pom.xml index 8b3600b..4a75a44 100644 --- a/strongbox-db-schema/pom.xml +++ b/strongbox-db-schema/pom.xml @@ -8,7 +8,7 @@ org.carlspring.strongbox strongbox-db - 1.0-PR-19-SNAPSHOT + 1.0-PR-20-SNAPSHOT ../pom.xml diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java index bc01849..ff24aab 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java @@ -4,9 +4,15 @@ public interface Edges { String ARTIFACT_HAS_ARTIFACT_COORDINATES = "ArtifactHasArtifactCoordinates"; + String ARTIFACT_HAS_TAGS = "ArtifactHasTags"; + String ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES = "ArtifactCoordinatesInheritGenericArtifactCoordinates"; + String ARTIFACT_GROUP_HAS_ARTIFACTS = "ArtifactGroupHasArtifacts"; + String REMOTE_ARTIFACT_INHERIT_ARTIFACT = "RemoteArtifactInheritArtifact"; + String ARTIFACT_HAS_ARTIFACT_ARCHIVE_LISTING = "ArtifactHasArtifactArchiveListing"; + } diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java index e7addc7..2321905 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java @@ -45,4 +45,4 @@ public interface Properties String COORDINATES_PACKAGE_TYPE = "coordinates.package_type"; String COORDINATES_LANGUAGE_IMPLEMENTATION_VERSION = "coordinates.languageImplementationVersion"; -} +} \ No newline at end of file diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java index cd7485b..a67c0ba 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java @@ -143,8 +143,7 @@ public void createSchema(JanusGraph jg) try { logger.info(String.format("Schema: %n%s", jgm.printSchema())); - } - finally + } finally { jgm.rollback(); } @@ -243,7 +242,7 @@ protected Set createIndexes(JanusGraph jg, Vertex.class, jgm.getVertexLabel(USER), true, - jgm.getPropertyKey(UUID)).ifPresent(result::add); + jgm.getPropertyKey(UUID)).ifPresent(result::add); return result; } @@ -520,10 +519,11 @@ private Optional buildIndexIfNecessary(final JanusGraphManagement jgm, final Class elementType, final JanusGraphSchemaType schemaType, final boolean unique, - final PropertyKey... properties){ + final PropertyKey... properties) + { return buildIndexIfNecessary(jgm, elementType, schemaType, unique, true, properties); } - + private Optional buildIndexIfNecessary(final JanusGraphManagement jgm, final Class elementType, final JanusGraphSchemaType schemaType, @@ -588,16 +588,16 @@ private void makeVertexLabelIfDoesNotExist(final JanusGraphManagement jgm, } private Optional makePropertyKeyIfDoesNotExist(final JanusGraphManagement jgm, - final String name, - final Class dataType) + final String name, + final Class dataType) { return makePropertyKeyIfDoesNotExist(jgm, name, dataType, null); } private Optional makePropertyKeyIfDoesNotExist(final JanusGraphManagement jgm, - final String name, - final Class dataType, - final Cardinality cardinality) + final String name, + final Class dataType, + final Cardinality cardinality) { if (jgm.containsPropertyKey(name)) { @@ -612,4 +612,4 @@ private Optional makePropertyKeyIfDoesNotExist(final JanusGraphMana return Optional.of(propertyKeyMaker.make()); } -} +} \ No newline at end of file diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java index 1e2cc25..06fda80 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java @@ -16,4 +16,4 @@ public interface Vertices String ARTIFACT_TAG = "ArtifactTag"; String ARTIFACT_ID_GROUP = "ArtifactIdGroup"; String USER = "User"; -} +} \ No newline at end of file diff --git a/strongbox-db-server/pom.xml b/strongbox-db-server/pom.xml index bb7ad65..35c4d00 100644 --- a/strongbox-db-server/pom.xml +++ b/strongbox-db-server/pom.xml @@ -9,7 +9,7 @@ org.carlspring.strongbox strongbox-db - 1.0-PR-19-SNAPSHOT + 1.0-PR-20-SNAPSHOT ../pom.xml From 70310217fbeb785d2b1ec306902b7d591ded3666 Mon Sep 17 00:00:00 2001 From: Ankit Tomar Date: Wed, 29 Apr 2020 00:00:36 +0530 Subject: [PATCH 3/5] Adding Schema Constraints for ArtifactArchiveListing Vertex --- .../carlspring/strongbox/db/schema/Edges.java | 5 ----- .../strongbox/db/schema/Properties.java | 3 +-- .../strongbox/db/schema/StrongboxSchema.java | 21 ++++++++++++++++--- .../strongbox/db/schema/Vertices.java | 2 ++ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java index ff24aab..53ecebe 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Edges.java @@ -4,15 +4,10 @@ public interface Edges { String ARTIFACT_HAS_ARTIFACT_COORDINATES = "ArtifactHasArtifactCoordinates"; - String ARTIFACT_HAS_TAGS = "ArtifactHasTags"; - String ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES = "ArtifactCoordinatesInheritGenericArtifactCoordinates"; - String ARTIFACT_GROUP_HAS_ARTIFACTS = "ArtifactGroupHasArtifacts"; - String REMOTE_ARTIFACT_INHERIT_ARTIFACT = "RemoteArtifactInheritArtifact"; - String ARTIFACT_HAS_ARTIFACT_ARCHIVE_LISTING = "ArtifactHasArtifactArchiveListing"; } diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java index 2321905..9fb1fa8 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Properties.java @@ -18,13 +18,12 @@ public interface Properties String CHECKSUMS = "checksums"; String CACHED = "cached"; String VERSION = "version"; - String USERNAME = "username"; String PASSWORD = "password"; String ENABLED = "enabled"; String ROLES = "roles"; String SECURITY_TOKEN_KEY = "securityTokenKey"; String SOURCE_ID = "sourceId"; - String FILE_NAMES = "filenames"; + String FILE_NAME = "fileName"; String COORDINATES_EXTENSION = "coordinates.extension"; String COORDINATES_NAME = "coordinates.name"; String COORDINATES_GROUP_ID = "coordinates.groupId"; diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java index a67c0ba..a571652 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java @@ -2,6 +2,7 @@ import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES; import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_GROUP_HAS_ARTIFACTS; +import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_HAS_ARTIFACT_ARCHIVE_LISTING; import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_HAS_ARTIFACT_COORDINATES; import static org.carlspring.strongbox.db.schema.Edges.ARTIFACT_HAS_TAGS; import static org.carlspring.strongbox.db.schema.Edges.REMOTE_ARTIFACT_INHERIT_ARTIFACT; @@ -29,7 +30,7 @@ import static org.carlspring.strongbox.db.schema.Properties.CREATED; import static org.carlspring.strongbox.db.schema.Properties.DOWNLOAD_COUNT; import static org.carlspring.strongbox.db.schema.Properties.ENABLED; -import static org.carlspring.strongbox.db.schema.Properties.FILE_NAMES; +import static org.carlspring.strongbox.db.schema.Properties.FILE_NAME; import static org.carlspring.strongbox.db.schema.Properties.LAST_UPDATED; import static org.carlspring.strongbox.db.schema.Properties.LAST_USED; import static org.carlspring.strongbox.db.schema.Properties.NAME; @@ -43,6 +44,7 @@ import static org.carlspring.strongbox.db.schema.Properties.UUID; import static org.carlspring.strongbox.db.schema.Properties.VERSION; import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT; +import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_ARCHIVE_LISTING; import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_COORDINATES; import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_ID_GROUP; import static org.carlspring.strongbox.db.schema.Vertices.ARTIFACT_TAG; @@ -265,6 +267,7 @@ private void applySchemaChanges(JanusGraphManagement jgm) makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_TAG); makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_ID_GROUP); makeVertexLabelIfDoesNotExist(jgm, USER); + makeVertexLabelIfDoesNotExist(jgm, ARTIFACT_ARCHIVE_LISTING); // Edges makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_HAS_ARTIFACT_COORDINATES, MANY2ONE); @@ -272,6 +275,7 @@ private void applySchemaChanges(JanusGraphManagement jgm) makeEdgeLabelIfDoesNotExist(jgm, REMOTE_ARTIFACT_INHERIT_ARTIFACT, ONE2ONE); makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_COORDINATES_INHERIT_GENERIC_ARTIFACT_COORDINATES, ONE2ONE); makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_GROUP_HAS_ARTIFACTS, ONE2MANY); + makeEdgeLabelIfDoesNotExist(jgm, ARTIFACT_HAS_ARTIFACT_ARCHIVE_LISTING, ONE2MANY); // Add property constraints applyPropertyConstraints(jgm); @@ -318,6 +322,10 @@ private void applyConnectionConstraints(JanusGraphManagement jgm) jgm.getVertexLabel(PYPI_ARTIFACT_COORDINATES), jgm.getVertexLabel(GENERIC_ARTIFACT_COORDINATES)); + jgm.addConnection(jgm.getEdgeLabel(ARTIFACT_HAS_ARTIFACT_ARCHIVE_LISTING), + jgm.getVertexLabel(ARTIFACT), + jgm.getVertexLabel(ARTIFACT_ARCHIVE_LISTING)); + } private void applyPropertyConstraints(JanusGraphManagement jgm) @@ -333,7 +341,6 @@ private void applyPropertyConstraints(JanusGraphManagement jgm) LAST_USED, SIZE_IN_BYTES, DOWNLOAD_COUNT, - FILE_NAMES, CHECKSUMS); addVertexPropertyConstraints(jgm, @@ -437,6 +444,14 @@ private void applyPropertyConstraints(JanusGraphManagement jgm) SOURCE_ID, CREATED, LAST_UPDATED); + + addVertexPropertyConstraints(jgm, + ARTIFACT_ARCHIVE_LISTING, + UUID, + STORAGE_ID, + REPOSITORY_ID, + FILE_NAME, + CREATED); } private void addVertexPropertyConstraints(JanusGraphManagement jgm, @@ -464,7 +479,7 @@ private void createProperties(JanusGraphManagement jgm) makePropertyKeyIfDoesNotExist(jgm, LAST_USED, Long.class, Cardinality.SINGLE); makePropertyKeyIfDoesNotExist(jgm, CREATED, Long.class, Cardinality.SINGLE); makePropertyKeyIfDoesNotExist(jgm, DOWNLOAD_COUNT, Integer.class, Cardinality.SINGLE); - makePropertyKeyIfDoesNotExist(jgm, FILE_NAMES, String.class, Cardinality.SET); + makePropertyKeyIfDoesNotExist(jgm, FILE_NAME, String.class, Cardinality.SINGLE); makePropertyKeyIfDoesNotExist(jgm, CHECKSUMS, String.class, Cardinality.SET); // RemoteArtifact diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java index 06fda80..ded0aff 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/Vertices.java @@ -16,4 +16,6 @@ public interface Vertices String ARTIFACT_TAG = "ArtifactTag"; String ARTIFACT_ID_GROUP = "ArtifactIdGroup"; String USER = "User"; + String ARTIFACT_ARCHIVE_LISTING = "ArtifactArchiveListing"; + } \ No newline at end of file From 5ccc5db74c63382839ac2b34cf3b4c1f605f38f8 Mon Sep 17 00:00:00 2001 From: Ankit Tomar Date: Wed, 29 Apr 2020 00:09:05 +0530 Subject: [PATCH 4/5] Adding indexes on uuid for ArtifactArchiveListing --- .../org/carlspring/strongbox/db/schema/StrongboxSchema.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java index a571652..52320f3 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java @@ -245,6 +245,11 @@ protected Set createIndexes(JanusGraph jg, jgm.getVertexLabel(USER), true, jgm.getPropertyKey(UUID)).ifPresent(result::add); + buildIndexIfNecessary(jgm, + Vertex.class, + jgm.getVertexLabel(ARTIFACT_ARCHIVE_LISTING), + true, + jgm.getPropertyKey(UUID)).ifPresent(result::add); return result; } From e12d6afcbd22918e4cadbb3b7c0817c3695d8406 Mon Sep 17 00:00:00 2001 From: Ankit Tomar Date: Thu, 30 Apr 2020 16:11:44 +0530 Subject: [PATCH 5/5] Setting uniquesness as false in uuid Index --- .../org/carlspring/strongbox/db/schema/StrongboxSchema.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java index 52320f3..cf7c037 100644 --- a/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java +++ b/strongbox-db-schema/src/main/java/org/carlspring/strongbox/db/schema/StrongboxSchema.java @@ -248,7 +248,7 @@ protected Set createIndexes(JanusGraph jg, buildIndexIfNecessary(jgm, Vertex.class, jgm.getVertexLabel(ARTIFACT_ARCHIVE_LISTING), - true, + false, jgm.getPropertyKey(UUID)).ifPresent(result::add); return result;