From c6e2ebffb3abeb885973a56f55413f7f156ef988 Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Fri, 3 Mar 2023 19:20:56 +0800
Subject: [PATCH 01/11] Added a new CypherAPI that is enhanced by
cypher-gremlin-extensions
---
.../hugegraph/api/cypher/CypherAPI.java | 159 ++++++++++
.../hugegraph/api/cypher/CypherClient.java | 146 +++++++++
.../hugegraph/api/cypher/CypherManager.java | 107 +++++++
.../hugegraph/api/cypher/CypherModel.java | 62 ++++
.../api/cypher/CypherOpProcessor.java | 283 ++++++++++++++++++
.../hugegraph/api/cypher/CypherPlugin.java | 67 +++++
.../hugegraph/api/gremlin/CypherAPI.java | 5 +-
.../hugegraph/auth/StandardAuthenticator.java | 82 ++++-
...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 1 +
...pache.tinkerpop.gremlin.server.OpProcessor | 1 +
.../java/org/apache/hugegraph/HugeGraph.java | 5 +-
.../hugegraph/auth/StandardAuthManager.java | 7 +-
.../optimize/HugePrimaryKeyStrategy.java | 109 +++++++
.../assembly/static/conf/gremlin-server.yaml | 14 +-
.../assembly/static/conf/remote-objects.yaml | 7 +-
15 files changed, 1045 insertions(+), 10 deletions(-)
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
create mode 100644 hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
create mode 100644 hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
create mode 100644 hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
new file mode 100644
index 0000000000..4ac6f3a6e4
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+
+import org.apache.hugegraph.HugeException;
+import org.apache.hugegraph.api.API;
+import org.apache.hugegraph.api.filter.CompressInterceptor;
+import org.apache.hugegraph.util.E;
+import org.apache.hugegraph.util.Log;
+import com.codahale.metrics.annotation.Timed;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+@Path("graphs/{graph}/cypher")
+@Singleton
+public class CypherAPI extends API {
+ private static final Logger LOG = Log.logger(CypherAPI.class);
+ private static final Charset UTF8 = Charset.forName(StandardCharsets.UTF_8.name());
+ private final Base64.Decoder decoder = Base64.getUrlDecoder();
+ private final String basic = "Basic ";
+ private final String bearer = "Bearer ";
+
+ private CypherManager cypherManager;
+
+ private CypherManager cypherManager() {
+ if (this.cypherManager == null) {
+ this.cypherManager = CypherManager.configOf("conf/remote-objects.yaml");
+ }
+ return this.cypherManager;
+ }
+
+ @GET
+ @Timed
+ @CompressInterceptor.Compress(buffer = (1024 * 40))
+ @Produces(APPLICATION_JSON_WITH_CHARSET)
+ public CypherModel query(@PathParam("graph") String graph,
+ @Context HttpHeaders headers,
+ @QueryParam("cypher") String cypher) {
+ LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
+
+ return this.queryByCypher(graph, headers, cypher);
+ }
+
+ @POST
+ @Timed
+ @CompressInterceptor.Compress
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON_WITH_CHARSET)
+ public CypherModel post(@PathParam("graph") String graph,
+ @Context HttpHeaders headers,
+ String cypher) {
+ LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
+ return this.queryByCypher(graph, headers, cypher);
+ }
+
+ private CypherModel queryByCypher(String graph,
+ HttpHeaders headers,
+ String cypher) {
+
+ E.checkArgument(graph != null && !graph.isEmpty(),
+ "The graph parameter can't be null or empty");
+ E.checkArgument(cypher != null && !cypher.isEmpty(),
+ "The cypher parameter can't be null or empty");
+
+ Map aliases = new HashMap<>(1, 1);
+ aliases.put("g", "__g_" + graph);
+
+ return this.client(headers).submitQuery(cypher, aliases);
+ }
+
+ private CypherClient client(HttpHeaders headers) {
+ String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
+
+ if (auth != null && !auth.isEmpty()) {
+ auth = auth.split(",")[0];
+ }
+
+ if (auth == null) {
+ throw new HugeException("The Cypher-API is being called without any authorization.");
+ }
+
+ if (auth.startsWith(basic)) {
+ return this.clientViaBasic(auth);
+ } else if (auth.startsWith(bearer)) {
+ return this.clientViaToken(auth);
+ }
+
+ throw new HugeException("The Cypher-API is being called without any authorization.");
+ }
+
+ private CypherClient clientViaBasic(String auth) {
+ Pair userPass = this.toUserPass(auth);
+ E.checkNotNull(userPass, "user-password-pair");
+
+ return this.cypherManager().getClient(userPass.getLeft(), userPass.getRight());
+ }
+
+ private CypherClient clientViaToken(String auth) {
+ return this.cypherManager().getClient(auth.substring(bearer.length()));
+ }
+
+ private Pair toUserPass(String auth) {
+ if (auth == null || auth.isEmpty()) return null;
+ if (!auth.startsWith(basic)) return null;
+
+ String[] split = null;
+
+ try {
+ String encoded = auth.substring(basic.length());
+ byte[] userPass = this.decoder.decode(encoded);
+ String authorization = new String(userPass, UTF8);
+ split = authorization.split(":");
+ } catch (Exception e) {
+ LOG.error("Failed convert auth to credential.", e);
+ return null;
+ }
+
+ if (split.length != 2) {
+ return null;
+ }
+
+ return ImmutablePair.of(split[0], split[1]);
+ }
+
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
new file mode 100644
index 0000000000..e282cf9940
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import org.apache.hugegraph.util.E;
+import org.apache.hugegraph.util.Log;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.driver.*;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.slf4j.Logger;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Supplier;
+
+@ThreadSafe
+public final class CypherClient {
+ private static final Logger LOG = Log.logger(CypherClient.class);
+ private String userName;
+ private String password;
+ private String token;
+ private Supplier configurationSupplier;
+
+ CypherClient(String userName, String password,
+ Supplier configurationSupplier) {
+ this.userName = userName;
+ this.password = password;
+ this.configurationSupplier = configurationSupplier;
+ }
+
+ CypherClient(String token, Supplier configurationSupplier) {
+ this.token = token;
+ this.configurationSupplier = configurationSupplier;
+ }
+
+ public CypherModel submitQuery(String cypherQuery,@Nullable Map aliases) {
+ E.checkArgument(cypherQuery != null && !cypherQuery.isEmpty(),
+ "The cypher-query parameter can't be null or empty");
+
+ Cluster cluster = Cluster.open(getConfig());
+ Client client = cluster.connect();
+
+ if (aliases != null && !aliases.isEmpty()) {
+ client = client.alias(aliases);
+ }
+
+ RequestMessage request = createRequest(cypherQuery);
+ CypherModel res = null;
+
+ try {
+ List list = this.doQueryList(client, request);
+ res = CypherModel.dataOf(request.getRequestId().toString(), list);
+ } catch (Exception e) {
+ LOG.error(String.format("Failed to submit cypher-query: [ %s ], cause by:"
+ , cypherQuery), e);
+ res = CypherModel.failOf(request.getRequestId().toString(), e.getMessage());
+ } finally {
+ client.close();
+ cluster.close();
+ }
+
+ return res;
+ }
+
+ private RequestMessage createRequest(String cypherQuery) {
+ return RequestMessage.build(Tokens.OPS_EVAL)
+ .processor("cypher")
+ .add(Tokens.ARGS_GREMLIN, cypherQuery)
+ .create();
+ }
+
+ private List doQueryList(Client client, RequestMessage request)
+ throws ExecutionException, InterruptedException {
+
+ ResultSet results = null;
+ results = client.submitAsync(request).get();
+
+ Iterator iter = results.iterator();
+ List list = new LinkedList<>();
+
+ for (; iter.hasNext(); ) {
+ Result data = iter.next();
+ list.add(data.getObject());
+ }
+
+ return list;
+ }
+
+ /**
+ * As Sasl does not support a token, which is a coded string to indicate a legal user,
+ * we had to use a trick to fix it. When the token is set, the password will be set to
+ * an empty string, which is an uncommon value under normal conditions.
+ * The token will then be transferred through the userName-property.
+ * To see org.apache.hugegraph.auth.StandardAuthenticator.PlainTextSaslAuthenticator
+ */
+ private Configuration getConfig() {
+ Configuration conf = this.configurationSupplier.get();
+ conf.addProperty("username", this.token == null ? this.userName : this.token);
+ conf.addProperty("password", this.token == null ? this.password : "");
+
+ return conf;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CypherClient that = (CypherClient) o;
+
+ return Objects.equals(userName, that.userName)
+ && Objects.equals(password, that.password)
+ && Objects.equals(token, that.token);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userName, password, token);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("CypherClient{");
+ sb.append("userName='").append(userName).append('\'');
+ sb.append(", token='").append(token).append('\'');
+ sb.append('}');
+
+ return sb.toString();
+ }
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
new file mode 100644
index 0000000000..173cf1d939
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import org.apache.hugegraph.util.E;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.YAMLConfiguration;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+
+import java.net.URL;
+
+@ThreadSafe
+public final class CypherManager {
+ private String configurationFile;
+ private YAMLConfiguration configuration;
+
+ public static CypherManager configOf(String configurationFile) {
+ E.checkArgument(configurationFile != null && !configurationFile.isEmpty(),
+ "The configurationFile parameter can't be null or empty");
+ return new CypherManager(configurationFile);
+ }
+
+ private CypherManager(String configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+
+ public CypherClient getClient(String userName, String password) {
+ E.checkArgument(userName != null && !userName.isEmpty(),
+ "The userName parameter can't be null or empty");
+ E.checkArgument(password != null && !password.isEmpty(),
+ "The password parameter can't be null or empty");
+
+ //TODO: Need to cache the client and make it hold the connection.
+ return new CypherClient(userName, password, () -> this.cloneConfig());
+ }
+
+ public CypherClient getClient(String token) {
+ E.checkArgument(token != null && !token.isEmpty(),
+ "The token parameter can't be null or empty");
+
+ //TODO: Need to cache the client and make it hold the connection.
+ return new CypherClient(token, () -> this.cloneConfig());
+ }
+
+ private Configuration cloneConfig() {
+ if (this.configuration == null) {
+ this.configuration = loadYaml(this.configurationFile);
+ }
+
+ return (Configuration) this.configuration.clone();
+ }
+
+ private static YAMLConfiguration loadYaml(String configurationFile) {
+ File yamlFile = getConfigFile(configurationFile);
+ YAMLConfiguration yaml;
+
+ try {
+ Reader reader = new FileReader(yamlFile);
+ yaml = new YAMLConfiguration();
+ yaml.read(reader);
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Failed to load configuration file," +
+ " the file at %s.", configurationFile), e);
+ }
+
+ return yaml;
+ }
+
+ private static File getConfigFile(String configurationFile) {
+ final File systemFile = new File(configurationFile);
+
+ if (!systemFile.exists()) {
+ final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+ final URL resource = currentClassLoader.getResource(configurationFile);
+ final File resourceFile = new File(resource.getFile());
+
+ if (!resourceFile.exists()) {
+ throw new IllegalArgumentException(String.format("Configuration file at %s does not exist"
+ , configurationFile));
+ }
+ return resourceFile;
+
+ }
+
+ return systemFile;
+ }
+
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
new file mode 100644
index 0000000000..31230248ef
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * As same as response of GremlinAPI
+ */
+public class CypherModel {
+ public String requestId;
+ public Status status = new Status();
+ public Result result = new Result();
+
+ public static CypherModel dataOf(String requestId, List data) {
+ CypherModel res = new CypherModel();
+ res.requestId = requestId;
+ res.status.code = 200;
+ res.result.data = data;
+ return res;
+ }
+
+ public static CypherModel failOf(String requestId, String message) {
+ CypherModel res = new CypherModel();
+ res.requestId = requestId;
+ res.status.code = 400;
+ res.status.message = message;
+ return res;
+ }
+
+ private CypherModel() {
+ }
+
+ public class Status {
+ public String message = "";
+ public int code;
+ public Map attributes = Collections.EMPTY_MAP;
+ }
+
+ private class Result {
+ public List data;
+ public Map meta = Collections.EMPTY_MAP;
+ }
+
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
new file mode 100644
index 0000000000..99bbb47bb6
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2018-2019 "Neo4j, Inc." [https://neo4j.com]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException;
+import org.apache.tinkerpop.gremlin.server.Context;
+import org.apache.tinkerpop.gremlin.server.GraphManager;
+import org.apache.tinkerpop.gremlin.server.OpProcessor;
+import org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor;
+import org.apache.tinkerpop.gremlin.server.op.OpProcessorException;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.util.function.ThrowingConsumer;
+import org.opencypher.gremlin.translation.CypherAst;
+import org.opencypher.gremlin.translation.groovy.GroovyPredicate;
+import org.opencypher.gremlin.translation.ir.TranslationWriter;
+import org.opencypher.gremlin.translation.ir.model.GremlinStep;
+import org.opencypher.gremlin.translation.translator.Translator;
+import org.opencypher.gremlin.traversal.ParameterNormalizer;
+import org.opencypher.gremlin.traversal.ProcedureContext;
+import org.opencypher.gremlin.traversal.ReturnNormalizer;
+import org.slf4j.Logger;
+import scala.collection.Seq;
+
+import java.util.*;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonList;
+import static java.util.Optional.empty;
+import static org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode.SERVER_ERROR;
+import static org.opencypher.gremlin.translation.StatementOption.EXPLAIN;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * {@link OpProcessor} implementation for processing Cypher {@link RequestMessage}s:
+ *
+ * {
+ * "requestId": "<some UUID>",
+ * "op": "eval",
+ * "processor": "cypher",
+ * "args": { "gremlin": "<CYPHER QUERY>" }
+ * }
+ *
+ */
+public class CypherOpProcessor extends AbstractEvalOpProcessor {
+ private static final String DEFAULT_TRANSLATOR_DEFINITION =
+ "gremlin+cfog_server_extensions+inline_parameters";
+
+ private static final Logger logger = getLogger(CypherOpProcessor.class);
+
+ public CypherOpProcessor() {
+ super(true);
+ }
+
+ @Override
+ public String getName() {
+ return "cypher";
+ }
+
+ @Override
+ public ThrowingConsumer getEvalOp() {
+ return this::evalCypher;
+ }
+
+ @Override
+ public Optional> selectOther(Context ctx)
+ throws OpProcessorException {
+ return empty();
+ }
+
+ private void evalCypher(Context context) throws OpProcessorException {
+ Map args = context.getRequestMessage().getArgs();
+ String cypher = (String) args.get(Tokens.ARGS_GREMLIN);
+ logger.info("Cypher: {}", cypher.replaceAll("\n", " "));
+
+ GraphTraversalSource gts = traversal(context);
+ DefaultGraphTraversal g = new DefaultGraphTraversal(gts.clone());
+ Map parameters = ParameterNormalizer.normalize(getParameters(args));
+ ProcedureContext procedureContext = ProcedureContext.global();
+ CypherAst ast = CypherAst.parse(cypher, parameters, procedureContext.getSignatures());
+
+ String translatorDefinition = getTranslatorDefinition(context);
+
+ Translator stringTranslator = Translator.builder()
+ .gremlinGroovy()
+ .build(translatorDefinition);
+
+ Translator traversalTranslator = Translator.builder()
+ .traversal(g)
+ .build(translatorDefinition);
+
+ Seq ir = ast.translate(stringTranslator.flavor(),
+ stringTranslator.features(), procedureContext);
+
+ String gremlin = TranslationWriter.write(ir, stringTranslator, parameters);
+ logger.info("Gremlin: {}", gremlin);
+
+ if (ast.getOptions().contains(EXPLAIN)) {
+ explainQuery(context, ast, gremlin);
+ return;
+ }
+
+ GraphTraversal, ?> traversal = TranslationWriter.write(ir, traversalTranslator, parameters);
+ ReturnNormalizer returnNormalizer = ReturnNormalizer.create(ast.getReturnTypes());
+ Iterator normalizedTraversal = returnNormalizer.normalize(traversal);
+ inTransaction(gts, () -> handleIterator(context, normalizedTraversal));
+ }
+
+ private void inTransaction(GraphTraversalSource gts, Runnable runnable) {
+ Graph graph = gts.getGraph();
+ boolean supportsTransactions = graph.features().graph().supportsTransactions();
+
+ if (!supportsTransactions) {
+ runnable.run();
+ return;
+ }
+
+ try {
+ graph.tx().open();
+ runnable.run();
+ graph.tx().commit();
+ } catch (Exception e) {
+ if (graph.tx().isOpen()) {
+ graph.tx().rollback();
+ }
+ }
+ }
+
+ private GraphTraversalSource traversal(Context context) throws OpProcessorException {
+ RequestMessage msg = context.getRequestMessage();
+ GraphManager graphManager = context.getGraphManager();
+
+ Optional> aliasesOptional = msg.optionalArgs(Tokens.ARGS_ALIASES);
+ String gAlias = aliasesOptional
+ .map(aliases -> aliases.get(Tokens.VAL_TRAVERSAL_SOURCE_ALIAS))
+ .orElse(null);
+
+ if (gAlias == null) {
+ return graphManager.getGraphNames().stream()
+ .sorted()
+ .findFirst()
+ .map(graphManager::getGraph)
+ .map(Graph::traversal)
+ .orElseThrow(() -> opProcessorException(msg, "No graphs found on the server"));
+ }
+
+ Graph graph = graphManager.getGraph(gAlias);
+ if (graph != null) {
+ return graph.traversal();
+ }
+
+ TraversalSource traversalSource = graphManager.getTraversalSource(gAlias);
+ if (traversalSource instanceof GraphTraversalSource) {
+ return (GraphTraversalSource) traversalSource;
+ }
+
+ throw opProcessorException(msg, "Traversable alias '" + gAlias + "' not found");
+ }
+
+ private OpProcessorException opProcessorException(RequestMessage msg, String errorMessage) {
+ return new OpProcessorException(errorMessage,
+ ResponseMessage.build(msg)
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage).create());
+ }
+
+ protected void handleIterator(Context context, Iterator traversal) {
+ RequestMessage msg = context.getRequestMessage();
+ final long timeout = msg.getArgs().containsKey(Tokens.ARGS_EVAL_TIMEOUT)
+ ? ((Number) msg.getArgs().get(Tokens.ARGS_EVAL_TIMEOUT)).longValue()
+ : context.getSettings().evaluationTimeout;
+
+ FutureTask evalFuture = new FutureTask<>(() -> {
+ try {
+ super.handleIterator(context, traversal);
+ } catch (Exception ex) {
+ String errorMessage = getErrorMessage(msg, ex);
+
+ logger.error("Error during traversal iteration", ex);
+ ChannelHandlerContext ctx = context.getChannelHandlerContext();
+ ctx.writeAndFlush(ResponseMessage.build(msg)
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex)
+ .create());
+ }
+ return null;
+ }
+ );
+
+ final Future> executionFuture = context.getGremlinExecutor()
+ .getExecutorService().submit(evalFuture);
+ if (timeout > 0) {
+ context.getScheduledExecutorService().schedule(
+ () -> executionFuture.cancel(true)
+ , timeout, TimeUnit.MILLISECONDS);
+ }
+
+ }
+
+ private String getErrorMessage(RequestMessage msg, Exception ex) {
+ if (ex instanceof InterruptedException || ex instanceof TraversalInterruptedException) {
+ return String.format("A timeout occurred during traversal evaluation of [%s] " +
+ "- consider increasing the limit given to scriptEvaluationTimeout", msg);
+ } else {
+ return ex.getMessage();
+ }
+ }
+
+ private void explainQuery(Context context, CypherAst ast, String gremlin) {
+ Map explanation = new LinkedHashMap<>();
+ explanation.put("translation", gremlin);
+ explanation.put("options", ast.getOptions().toString());
+
+ ResponseMessage explainMsg = ResponseMessage.build(context.getRequestMessage())
+ .code(ResponseStatusCode.SUCCESS)
+ .statusMessage("OK")
+ .result(singletonList(explanation))
+ .create();
+
+ ChannelHandlerContext ctx = context.getChannelHandlerContext();
+ ctx.writeAndFlush(explainMsg);
+ }
+
+ @Override
+ public void close() {
+ // do nothing = no resources to release
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map getParameters(Map args) {
+ if (args.containsKey(Tokens.ARGS_BINDINGS)) {
+ return (Map) args.get(Tokens.ARGS_BINDINGS);
+ } else {
+ return new HashMap<>();
+ }
+ }
+
+ private String getTranslatorDefinition(Context context) {
+ Map config = context.getSettings()
+ .optionalProcessor(CypherOpProcessor.class)
+ .map(p -> p.config)
+ .orElse(emptyMap());
+
+ HashSet properties = new HashSet<>(config.keySet());
+ properties.remove("translatorDefinition");
+ properties.remove("translatorFeatures");
+ if (!properties.isEmpty()) {
+ throw new IllegalStateException("Unknown configuration parameters " +
+ "found for CypherOpProcessor: " + properties);
+ }
+
+ return config.getOrDefault("translatorDefinition", DEFAULT_TRANSLATOR_DEFINITION)
+ + "+" + config.getOrDefault("translatorFeatures", "");
+ }
+
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
new file mode 100644
index 0000000000..fc6c2dda6e
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018-2019 "Neo4j, Inc." [https://neo4j.com]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.api.cypher;
+
+import org.apache.tinkerpop.gremlin.jsr223.Customizer;
+import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin;
+import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
+import org.opencypher.gremlin.traversal.CustomFunctions;
+import org.opencypher.gremlin.traversal.CustomPredicate;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CypherPlugin implements GremlinPlugin {
+
+ private static final ImportCustomizer imports = DefaultImportCustomizer.build()
+ .addClassImports(CustomPredicate.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomPredicate.class))
+ .addClassImports(CustomFunctions.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomFunctions.class))
+ .create();
+
+ private static List getDeclaredPublicMethods(Class> klass) {
+ Method[] declaredMethods = klass.getDeclaredMethods();
+ return Stream.of(declaredMethods)
+ .filter(method -> Modifier.isPublic(method.getModifiers()))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getName() {
+ return "cypher.extra";
+ }
+
+ public static GremlinPlugin instance() {
+ return new CypherPlugin();
+ }
+
+ @Override
+ public boolean requireRestart() {
+ return true;
+ }
+
+ @Override
+ public Optional getCustomizers(String scriptEngineName) {
+ return Optional.of(new Customizer[]{imports});
+ }
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
index 24c39a69fd..19802580ae 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
@@ -30,7 +30,7 @@
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
-import jakarta.ws.rs.Path;
+
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
@@ -38,9 +38,10 @@
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
-@Path("graphs/{graph}/cypher")
+//@Path("graphs/{graph}/cypher")
@Singleton
@Tag(name = "CypherAPI")
+@Deprecated
public class CypherAPI extends GremlinQueryAPI {
private static final Logger LOG = Log.logger(CypherAPI.class);
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
index 2cda2333e4..f5800af5ad 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
@@ -19,11 +19,15 @@
import java.io.Console;
import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
-import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
+import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
+import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
import org.apache.hugegraph.HugeGraph;
@@ -35,10 +39,13 @@
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.StringEncoding;
+import static org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens.PROPERTY_PASSWORD;
+import static org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens.PROPERTY_USERNAME;
+
public class StandardAuthenticator implements HugeAuthenticator {
private static final String INITING_STORE = "initing_store";
-
+ private static final byte NUL = 0;
private HugeGraph graph = null;
private HugeGraph graph() {
@@ -177,7 +184,7 @@ public AuthManager authManager() {
@Override
public SaslNegotiator newSaslNegotiator(InetAddress remoteAddress) {
- throw new NotImplementedException("SaslNegotiator is unsupported");
+ return new PlainTextSaslAuthenticator();
}
public static void initAdminUserIfNeeded(String confFile) throws Exception {
@@ -193,4 +200,73 @@ public static void initAdminUserIfNeeded(String confFile) throws Exception {
auth.initAdminUser();
}
}
+
+ /**
+ * Copied from org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator.PlainTextSaslAuthenticator.
+ * Added support of token.
+ */
+ private class PlainTextSaslAuthenticator implements SaslNegotiator {
+ private boolean complete = false;
+ private String username;
+ private String password;
+
+ private String token;
+
+ @Override
+ public byte[] evaluateResponse(final byte[] clientResponse) throws AuthenticationException {
+ decodeCredentials(clientResponse);
+ complete = true;
+ return null;
+ }
+
+ @Override
+ public boolean isComplete() {
+ return complete;
+ }
+
+ @Override
+ public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException {
+ if (!complete) throw new AuthenticationException("SASL negotiation not complete");
+ final Map credentials = new HashMap<>();
+ credentials.put(PROPERTY_USERNAME, username);
+ credentials.put(PROPERTY_PASSWORD, password);
+
+ credentials.put(KEY_TOKEN, token);
+
+ return authenticate(credentials);
+ }
+
+ /**
+ * SASL PLAIN mechanism specifies that credentials are encoded in a
+ * sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL).
+ * The form is : {code}authzIdauthnIdpassword{code}.
+ *
+ * @param bytes encoded credentials string sent by the client
+ */
+ private void decodeCredentials(byte[] bytes) throws AuthenticationException {
+ byte[] user = null;
+ byte[] pass = null;
+ int end = bytes.length;
+ for (int i = bytes.length - 1 ; i >= 0; i--) {
+ if (bytes[i] == NUL) {
+ if (pass == null)
+ pass = Arrays.copyOfRange(bytes, i + 1, end);
+ else if (user == null)
+ user = Arrays.copyOfRange(bytes, i + 1, end);
+ end = i;
+ }
+ }
+
+ if (null == user) throw new AuthenticationException("Authentication ID must not be null");
+ if (null == pass) throw new AuthenticationException("Password must not be null");
+
+ username = new String(user, StandardCharsets.UTF_8);
+ password = new String(pass, StandardCharsets.UTF_8);
+
+ /* The trick is here. >_*/
+ if(password.isEmpty()){
+ token=username;
+ }
+ }
+ }
}
diff --git a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
new file mode 100644
index 0000000000..be2a5bfad4
--- /dev/null
+++ b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
@@ -0,0 +1 @@
+org.apache.hugegraph.api.cypher.CypherPlugin
diff --git a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
new file mode 100644
index 0000000000..58b2be3401
--- /dev/null
+++ b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
@@ -0,0 +1 @@
+org.apache.hugegraph.api.cypher.CypherOpProcessor
diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java b/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java
index e2c2000ba7..ed094f6383 100644
--- a/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java
+++ b/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java
@@ -31,6 +31,7 @@
import org.apache.hugegraph.rpc.RpcServiceConfig4Client;
import org.apache.hugegraph.rpc.RpcServiceConfig4Server;
import org.apache.hugegraph.task.TaskScheduler;
+import org.apache.hugegraph.traversal.optimize.HugePrimaryKeyStrategy;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.type.define.GraphMode;
import org.apache.hugegraph.type.define.GraphReadMode;
@@ -317,7 +318,9 @@ static void registerTraversalStrategies(Class> clazz) {
.clone();
strategies.addStrategies(HugeVertexStepStrategy.instance(),
HugeGraphStepStrategy.instance(),
- HugeCountStepStrategy.instance());
+ HugeCountStepStrategy.instance(),
+ HugePrimaryKeyStrategy.instance());
+
TraversalStrategies.GlobalCache.registerStrategies(clazz, strategies);
}
}
diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
index 7f3e12d35e..910f19cdc5 100644
--- a/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
+++ b/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
@@ -665,7 +665,12 @@ public UserWithRole validateUser(String token) {
Claims payload = null;
boolean needBuildCache = false;
if (username == null) {
- payload = this.tokenGenerator.verify(token);
+ try{
+ payload = this.tokenGenerator.verify(token);
+ }catch (Throwable t){
+ LOG.error(String.format("Failed to verify token:[ %s ], cause:",token),t);
+ return new UserWithRole("");
+ }
username = (String) payload.get(AuthConstant.TOKEN_USER_NAME);
needBuildCache = true;
}
diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java b/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
new file mode 100644
index 0000000000..1dcffe9b07
--- /dev/null
+++ b/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.traversal.optimize;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy.ProviderOptimizationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality;
+
+import java.util.LinkedList;
+import java.util.List;
+
+
+public class HugePrimaryKeyStrategy
+ extends AbstractTraversalStrategy
+ implements ProviderOptimizationStrategy {
+
+ private static final long serialVersionUID = 6307847098226016416L;
+ private static final HugePrimaryKeyStrategy INSTANCE = new HugePrimaryKeyStrategy();
+
+ public static HugePrimaryKeyStrategy instance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void apply(Traversal.Admin, ?> traversal) {
+
+ List removeSteps = new LinkedList<>();
+ Mutating curAddStep = null;
+ List stepList = traversal.getSteps();
+
+ for (int i = 0, s = stepList.size(); i < s; i++) {
+ Step step = stepList.get(i);
+
+ if (i == 0 && AddVertexStartStep.class.isInstance(step)) {
+ curAddStep = (Mutating) step;
+ continue;
+ } else if (curAddStep == null && AddVertexStep.class.isInstance((step))) {
+ curAddStep = (Mutating) step;
+ continue;
+ }
+
+ if (curAddStep == null) continue;
+
+ if (!AddPropertyStep.class.isInstance(step)) {
+ curAddStep = null;
+ continue;
+ }
+
+ AddPropertyStep propertyStep = (AddPropertyStep) step;
+
+ if (propertyStep.getCardinality() == Cardinality.single
+ || propertyStep.getCardinality() == null) {
+
+ Object[] kvs = new Object[2];
+ List kvList = new LinkedList<>();
+
+ propertyStep.getParameters().getRaw().forEach((k, v) -> {
+ if (T.key.equals(k)) {
+ kvs[0] = v.get(0);
+ } else if (T.value.equals(k)) {
+ kvs[1] = v.get(0);
+ } else {
+ kvList.add(k.toString());
+ kvList.add(v.get(0));
+ }
+ });
+
+ curAddStep.configure(kvs);
+
+ if (kvList.size() > 0) {
+ curAddStep.configure(kvList.toArray(new Object[kvList.size()]));
+ }
+
+ removeSteps.add(step);
+ } else {
+ curAddStep = null;
+ }
+
+ }
+
+ for (Step index : removeSteps) {
+ traversal.removeStep(index);
+ }
+
+ }
+}
diff --git a/hugegraph-dist/src/assembly/static/conf/gremlin-server.yaml b/hugegraph-dist/src/assembly/static/conf/gremlin-server.yaml
index 61c3517e39..dff43cb04b 100644
--- a/hugegraph-dist/src/assembly/static/conf/gremlin-server.yaml
+++ b/hugegraph-dist/src/assembly/static/conf/gremlin-server.yaml
@@ -27,6 +27,10 @@ graphs: {
}
scriptEngines: {
gremlin-groovy: {
+ staticImports: [
+ org.opencypher.gremlin.process.traversal.CustomPredicates.*',
+ org.opencypher.gremlin.traversal.CustomFunctions.*
+ ],
plugins: {
org.apache.hugegraph.plugin.HugeGraphGremlinPlugin: {},
org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
@@ -60,9 +64,15 @@ scriptEngines: {
org.apache.hugegraph.traversal.optimize.ConditionP,
org.apache.hugegraph.traversal.optimize.Text,
org.apache.hugegraph.traversal.optimize.TraversalUtil,
- org.apache.hugegraph.util.DateUtil
+ org.apache.hugegraph.util.DateUtil,
+ org.opencypher.gremlin.traversal.CustomFunctions,
+ org.opencypher.gremlin.traversal.CustomPredicate
],
- methodImports: [java.lang.Math#*]
+ methodImports: [
+ java.lang.Math#*,
+ org.opencypher.gremlin.traversal.CustomPredicate#*,
+ org.opencypher.gremlin.traversal.CustomFunctions#*
+ ]
},
org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {
files: [scripts/empty-sample.groovy]
diff --git a/hugegraph-dist/src/assembly/static/conf/remote-objects.yaml b/hugegraph-dist/src/assembly/static/conf/remote-objects.yaml
index 55f38ab97d..8ba24d00a1 100644
--- a/hugegraph-dist/src/assembly/static/conf/remote-objects.yaml
+++ b/hugegraph-dist/src/assembly/static/conf/remote-objects.yaml
@@ -20,6 +20,11 @@ serializer: {
className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0,
config: {
serializeResultToString: false,
- ioRegistries: [org.apache.hugegraph.io.HugeGraphIoRegistry]
+ # The duplication of HugeGraphIoRegistry is meant to fix a bug in the
+ # 'org.apache.tinkerpop.gremlin.driver.Settings:from(Configuration)' method.
+ ioRegistries: [
+ org.apache.hugegraph.io.HugeGraphIoRegistry,
+ org.apache.hugegraph.io.HugeGraphIoRegistry
+ ]
}
}
From a1da03939062d7a1810fd92b47a02825f7f1e549 Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Mon, 6 Mar 2023 11:13:58 +0800
Subject: [PATCH 02/11] Removed the previous CypherAPI.
---
.../hugegraph/api/gremlin/CypherAPI.java | 112 ------------------
1 file changed, 112 deletions(-)
delete mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
deleted file mode 100644
index 19802580ae..0000000000
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.hugegraph.api.gremlin;
-
-import org.opencypher.gremlin.translation.TranslationFacade;
-import org.slf4j.Logger;
-
-import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
-import org.apache.hugegraph.util.E;
-import org.apache.hugegraph.util.Log;
-import com.codahale.metrics.annotation.Timed;
-
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.inject.Singleton;
-import jakarta.ws.rs.Consumes;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.QueryParam;
-import jakarta.ws.rs.core.Context;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.Response;
-
-//@Path("graphs/{graph}/cypher")
-@Singleton
-@Tag(name = "CypherAPI")
-@Deprecated
-public class CypherAPI extends GremlinQueryAPI {
-
- private static final Logger LOG = Log.logger(CypherAPI.class);
-
- @GET
- @Timed
- @Compress(buffer = (1024 * 40))
- @Produces(APPLICATION_JSON_WITH_CHARSET)
- public Response query(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- @QueryParam("cypher") String cypher) {
- LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
-
- return this.queryByCypher(graph, headers, cypher);
- }
-
- @POST
- @Timed
- @Compress
- @Consumes(APPLICATION_JSON)
- @Produces(APPLICATION_JSON_WITH_CHARSET)
- public Response post(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- String cypher) {
- LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
- return this.queryByCypher(graph, headers, cypher);
- }
-
- private Response queryByCypher(String graph,
- HttpHeaders headers,
- String cypher) {
- E.checkArgument(cypher != null && !cypher.isEmpty(),
- "The cypher parameter can't be null or empty");
-
- String gremlin = this.translateCpyher2Gremlin(graph, cypher);
- LOG.debug("translated gremlin is {}", gremlin);
-
- String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
- String request = "{" +
- "\"gremlin\":\"" + gremlin + "\"," +
- "\"bindings\":{}," +
- "\"language\":\"gremlin-groovy\"," +
- "\"aliases\":{\"g\":\"__g_" + graph + "\"}}";
-
- Response response = this.client().doPostRequest(auth, request);
- return transformResponseIfNeeded(response);
- }
-
- private String translateCpyher2Gremlin(String graph, String cypher) {
- TranslationFacade translator = new TranslationFacade();
- String gremlin = translator.toGremlinGroovy(cypher);
- gremlin = this.buildQueryableGremlin(graph, gremlin);
- return gremlin;
- }
-
- private String buildQueryableGremlin(String graph, String gremlin) {
- /*
- * `CREATE (a:person { name : 'test', age: 20) return a`
- * would be translated to:
- * `g.addV('person').as('a').property(single, 'name', 'test') ...`,
- * but hugegraph don't support `.property(single, k, v)`,
- * so we replace it to `.property(k, v)` here
- */
- gremlin = gremlin.replace(".property(single,", ".property(");
-
- return gremlin;
- }
-}
From 9a6fd4afe7fd4e2c01d6a13a023765ac97e9a5da Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Mon, 6 Mar 2023 18:12:46 +0800
Subject: [PATCH 03/11] Updated some license things and refactored the
implementation of SaslNegotiator.
---
.licenserc.yaml | 2 +
LICENSE | 2 +
.../api/cypher/CypherOpProcessor.java | 16 +++
.../hugegraph/api/cypher/CypherPlugin.java | 11 ++
.../hugegraph/api/gremlin/CypherAPI.java | 112 ++++++++++++++++++
.../hugegraph/auth/StandardAuthenticator.java | 57 ++++-----
6 files changed, 167 insertions(+), 33 deletions(-)
create mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
diff --git a/.licenserc.yaml b/.licenserc.yaml
index ea56bb6e15..6a98da9a5e 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -94,6 +94,8 @@ header: # `header` section is configurations for source codes license header.
- '**/type/Nameable.java'
- '**/define/Cardinality.java'
- '**/util/StringEncoding.java'
+ - 'hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java'
+ - 'hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java'
comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`.
# license-location-threshold specifies the index threshold where the license header can be located,
diff --git a/LICENSE b/LICENSE
index 84199011b5..0634009f19 100644
--- a/LICENSE
+++ b/LICENSE
@@ -214,3 +214,5 @@ hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugeScriptT
hugegraph-core/src/main/java/org/apache/hugegraph/type/Nameable.java from https://github.com/JanusGraph/janusgraph
hugegraph-core/src/main/java/org/apache/hugegraph/type/define/Cardinality.java from https://github.com/JanusGraph/janusgraph
hugegraph-core/src/main/java/org/apache/hugegraph/util/StringEncoding.java from https://github.com/JanusGraph/janusgraph
+hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java from https://github.com/opencypher/cypher-for-gremlin
+hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java from https://github.com/opencypher/cypher-for-gremlin
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
index 99bbb47bb6..c1d72ee532 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
@@ -14,6 +14,22 @@
* limitations under the License.
*/
+
+/**
+ * Description of the modifications:
+ *
+ * 1) Changed the method signature to adopt the gremlin-server 3.5.1.
+ * public Optional> selectOther(RequestMessage requestMessage)
+ * -->
+ * public Optional> selectOther(Context ctx)
+ *
+ * 2) Changed the package name.
+ * org.opencypher.gremlin.server.op.cypher
+ * -->
+ * org.apache.hugegraph.api.cypher
+ *
+ */
+
package org.apache.hugegraph.api.cypher;
import io.netty.channel.ChannelHandlerContext;
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
index fc6c2dda6e..ea06fb0361 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
@@ -14,6 +14,17 @@
* limitations under the License.
*/
+
+/**
+ * Description of the modifications:
+ *
+ * 1) Changed the package name.
+ * org.opencypher.gremlin.server.jsr223
+ * -->
+ * org.apache.hugegraph.api.cypher
+ *
+ */
+
package org.apache.hugegraph.api.cypher;
import org.apache.tinkerpop.gremlin.jsr223.Customizer;
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
new file mode 100644
index 0000000000..19802580ae
--- /dev/null
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.gremlin;
+
+import org.opencypher.gremlin.translation.TranslationFacade;
+import org.slf4j.Logger;
+
+import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
+import org.apache.hugegraph.util.E;
+import org.apache.hugegraph.util.Log;
+import com.codahale.metrics.annotation.Timed;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+
+//@Path("graphs/{graph}/cypher")
+@Singleton
+@Tag(name = "CypherAPI")
+@Deprecated
+public class CypherAPI extends GremlinQueryAPI {
+
+ private static final Logger LOG = Log.logger(CypherAPI.class);
+
+ @GET
+ @Timed
+ @Compress(buffer = (1024 * 40))
+ @Produces(APPLICATION_JSON_WITH_CHARSET)
+ public Response query(@PathParam("graph") String graph,
+ @Context HttpHeaders headers,
+ @QueryParam("cypher") String cypher) {
+ LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
+
+ return this.queryByCypher(graph, headers, cypher);
+ }
+
+ @POST
+ @Timed
+ @Compress
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON_WITH_CHARSET)
+ public Response post(@PathParam("graph") String graph,
+ @Context HttpHeaders headers,
+ String cypher) {
+ LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
+ return this.queryByCypher(graph, headers, cypher);
+ }
+
+ private Response queryByCypher(String graph,
+ HttpHeaders headers,
+ String cypher) {
+ E.checkArgument(cypher != null && !cypher.isEmpty(),
+ "The cypher parameter can't be null or empty");
+
+ String gremlin = this.translateCpyher2Gremlin(graph, cypher);
+ LOG.debug("translated gremlin is {}", gremlin);
+
+ String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
+ String request = "{" +
+ "\"gremlin\":\"" + gremlin + "\"," +
+ "\"bindings\":{}," +
+ "\"language\":\"gremlin-groovy\"," +
+ "\"aliases\":{\"g\":\"__g_" + graph + "\"}}";
+
+ Response response = this.client().doPostRequest(auth, request);
+ return transformResponseIfNeeded(response);
+ }
+
+ private String translateCpyher2Gremlin(String graph, String cypher) {
+ TranslationFacade translator = new TranslationFacade();
+ String gremlin = translator.toGremlinGroovy(cypher);
+ gremlin = this.buildQueryableGremlin(graph, gremlin);
+ return gremlin;
+ }
+
+ private String buildQueryableGremlin(String graph, String gremlin) {
+ /*
+ * `CREATE (a:person { name : 'test', age: 20) return a`
+ * would be translated to:
+ * `g.addV('person').as('a').property(single, 'name', 'test') ...`,
+ * but hugegraph don't support `.property(single, k, v)`,
+ * so we replace it to `.property(k, v)` here
+ */
+ gremlin = gremlin.replace(".property(single,", ".property(");
+
+ return gremlin;
+ }
+}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
index f5800af5ad..537148eb2c 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
@@ -39,13 +39,10 @@
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.StringEncoding;
-import static org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens.PROPERTY_PASSWORD;
-import static org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens.PROPERTY_USERNAME;
public class StandardAuthenticator implements HugeAuthenticator {
private static final String INITING_STORE = "initing_store";
- private static final byte NUL = 0;
private HugeGraph graph = null;
private HugeGraph graph() {
@@ -184,7 +181,7 @@ public AuthManager authManager() {
@Override
public SaslNegotiator newSaslNegotiator(InetAddress remoteAddress) {
- return new PlainTextSaslAuthenticator();
+ return new TokenSaslAuthenticator();
}
public static void initAdminUserIfNeeded(String confFile) throws Exception {
@@ -201,36 +198,31 @@ public static void initAdminUserIfNeeded(String confFile) throws Exception {
}
}
- /**
- * Copied from org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator.PlainTextSaslAuthenticator.
- * Added support of token.
- */
- private class PlainTextSaslAuthenticator implements SaslNegotiator {
- private boolean complete = false;
+ private class TokenSaslAuthenticator implements SaslNegotiator {
+ private static final byte NUL = 0;
private String username;
private String password;
-
private String token;
@Override
public byte[] evaluateResponse(final byte[] clientResponse) throws AuthenticationException {
- decodeCredentials(clientResponse);
- complete = true;
+ decode(clientResponse);
return null;
}
@Override
public boolean isComplete() {
- return complete;
+ return this.username != null;
}
@Override
public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException {
- if (!complete) throw new AuthenticationException("SASL negotiation not complete");
- final Map credentials = new HashMap<>();
- credentials.put(PROPERTY_USERNAME, username);
- credentials.put(PROPERTY_PASSWORD, password);
+ if (!this.isComplete())
+ throw new AuthenticationException("The SASL negotiation has not yet been completed.");
+ final Map credentials = new HashMap<>(6, 1);
+ credentials.put(KEY_USERNAME, username);
+ credentials.put(KEY_PASSWORD, password);
credentials.put(KEY_TOKEN, token);
return authenticate(credentials);
@@ -243,29 +235,28 @@ public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException {
*
* @param bytes encoded credentials string sent by the client
*/
- private void decodeCredentials(byte[] bytes) throws AuthenticationException {
- byte[] user = null;
- byte[] pass = null;
+ private void decode(byte[] bytes) throws AuthenticationException {
+ this.username = null;
+ this.password = null;
+
int end = bytes.length;
- for (int i = bytes.length - 1 ; i >= 0; i--) {
+
+ for (int i = bytes.length - 1; i >= 0; i--) {
if (bytes[i] == NUL) {
- if (pass == null)
- pass = Arrays.copyOfRange(bytes, i + 1, end);
- else if (user == null)
- user = Arrays.copyOfRange(bytes, i + 1, end);
+ if (this.password == null)
+ password = new String(Arrays.copyOfRange(bytes, i + 1, end), StandardCharsets.UTF_8);
+ else if (this.username == null)
+ username = new String(Arrays.copyOfRange(bytes, i + 1, end), StandardCharsets.UTF_8);
end = i;
}
}
- if (null == user) throw new AuthenticationException("Authentication ID must not be null");
- if (null == pass) throw new AuthenticationException("Password must not be null");
-
- username = new String(user, StandardCharsets.UTF_8);
- password = new String(pass, StandardCharsets.UTF_8);
+ if (this.username == null) throw new AuthenticationException("SASL authentication ID must not be null.");
+ if (this.password == null) throw new AuthenticationException("SASL password must not be null.");
/* The trick is here. >_*/
- if(password.isEmpty()){
- token=username;
+ if (password.isEmpty()) {
+ token = username;
}
}
}
From 7c7f321b77db7070a3340f09b1cbd5bb24f9da8d Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Mon, 6 Mar 2023 20:44:59 +0800
Subject: [PATCH 04/11] Made it into HugeGraph style and added CypherApiTest to
ApiTestSuite
---
.../hugegraph/api/cypher/CypherAPI.java | 16 ++--
.../hugegraph/api/cypher/CypherClient.java | 19 ++---
.../hugegraph/api/cypher/CypherManager.java | 16 ++--
.../api/cypher/CypherOpProcessor.java | 80 ++++++++++---------
.../hugegraph/api/cypher/CypherPlugin.java | 20 ++---
.../apache/hugegraph/api/ApiTestSuite.java | 3 +-
6 files changed, 82 insertions(+), 72 deletions(-)
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
index 4ac6f3a6e4..b68df4d97f 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
@@ -27,7 +27,9 @@
import org.apache.hugegraph.api.filter.CompressInterceptor;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.Log;
+
import com.codahale.metrics.annotation.Timed;
+
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
@@ -67,8 +69,7 @@ private CypherManager cypherManager() {
@Timed
@CompressInterceptor.Compress(buffer = (1024 * 40))
@Produces(APPLICATION_JSON_WITH_CHARSET)
- public CypherModel query(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
+ public CypherModel query(@PathParam("graph") String graph, @Context HttpHeaders headers,
@QueryParam("cypher") String cypher) {
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
@@ -80,21 +81,18 @@ public CypherModel query(@PathParam("graph") String graph,
@CompressInterceptor.Compress
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
- public CypherModel post(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
+ public CypherModel post(@PathParam("graph") String graph, @Context HttpHeaders headers,
String cypher) {
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
return this.queryByCypher(graph, headers, cypher);
}
- private CypherModel queryByCypher(String graph,
- HttpHeaders headers,
- String cypher) {
+ private CypherModel queryByCypher(String graph, HttpHeaders headers, String cypher) {
E.checkArgument(graph != null && !graph.isEmpty(),
- "The graph parameter can't be null or empty");
+ "The graph parameter can't be null or empty");
E.checkArgument(cypher != null && !cypher.isEmpty(),
- "The cypher parameter can't be null or empty");
+ "The cypher parameter can't be null or empty");
Map aliases = new HashMap<>(1, 1);
aliases.put("g", "__g_" + graph);
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
index e282cf9940..8669717bc3 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -26,6 +26,7 @@
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
+
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@@ -50,9 +51,9 @@ public final class CypherClient {
this.configurationSupplier = configurationSupplier;
}
- public CypherModel submitQuery(String cypherQuery,@Nullable Map aliases) {
+ public CypherModel submitQuery(String cypherQuery, @Nullable Map aliases) {
E.checkArgument(cypherQuery != null && !cypherQuery.isEmpty(),
- "The cypher-query parameter can't be null or empty");
+ "The cypher-query parameter can't be null or empty");
Cluster cluster = Cluster.open(getConfig());
Client client = cluster.connect();
@@ -69,7 +70,7 @@ public CypherModel submitQuery(String cypherQuery,@Nullable Map
res = CypherModel.dataOf(request.getRequestId().toString(), list);
} catch (Exception e) {
LOG.error(String.format("Failed to submit cypher-query: [ %s ], cause by:"
- , cypherQuery), e);
+ , cypherQuery), e);
res = CypherModel.failOf(request.getRequestId().toString(), e.getMessage());
} finally {
client.close();
@@ -81,13 +82,13 @@ public CypherModel submitQuery(String cypherQuery,@Nullable Map
private RequestMessage createRequest(String cypherQuery) {
return RequestMessage.build(Tokens.OPS_EVAL)
- .processor("cypher")
- .add(Tokens.ARGS_GREMLIN, cypherQuery)
- .create();
+ .processor("cypher")
+ .add(Tokens.ARGS_GREMLIN, cypherQuery)
+ .create();
}
private List doQueryList(Client client, RequestMessage request)
- throws ExecutionException, InterruptedException {
+ throws ExecutionException, InterruptedException {
ResultSet results = null;
results = client.submitAsync(request).get();
@@ -125,8 +126,8 @@ public boolean equals(Object o) {
CypherClient that = (CypherClient) o;
return Objects.equals(userName, that.userName)
- && Objects.equals(password, that.password)
- && Objects.equals(token, that.token);
+ && Objects.equals(password, that.password)
+ && Objects.equals(token, that.token);
}
@Override
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
index 173cf1d939..69c43a3f0b 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -22,6 +22,7 @@
import org.apache.commons.configuration2.YAMLConfiguration;
import javax.annotation.concurrent.ThreadSafe;
+
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
@@ -35,7 +36,7 @@ public final class CypherManager {
public static CypherManager configOf(String configurationFile) {
E.checkArgument(configurationFile != null && !configurationFile.isEmpty(),
- "The configurationFile parameter can't be null or empty");
+ "The configurationFile parameter can't be null or empty");
return new CypherManager(configurationFile);
}
@@ -45,9 +46,9 @@ private CypherManager(String configurationFile) {
public CypherClient getClient(String userName, String password) {
E.checkArgument(userName != null && !userName.isEmpty(),
- "The userName parameter can't be null or empty");
+ "The userName parameter can't be null or empty");
E.checkArgument(password != null && !password.isEmpty(),
- "The password parameter can't be null or empty");
+ "The password parameter can't be null or empty");
//TODO: Need to cache the client and make it hold the connection.
return new CypherClient(userName, password, () -> this.cloneConfig());
@@ -55,7 +56,7 @@ public CypherClient getClient(String userName, String password) {
public CypherClient getClient(String token) {
E.checkArgument(token != null && !token.isEmpty(),
- "The token parameter can't be null or empty");
+ "The token parameter can't be null or empty");
//TODO: Need to cache the client and make it hold the connection.
return new CypherClient(token, () -> this.cloneConfig());
@@ -79,7 +80,7 @@ private static YAMLConfiguration loadYaml(String configurationFile) {
yaml.read(reader);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to load configuration file," +
- " the file at %s.", configurationFile), e);
+ " the file at %s.", configurationFile), e);
}
return yaml;
@@ -94,8 +95,9 @@ private static File getConfigFile(String configurationFile) {
final File resourceFile = new File(resource.getFile());
if (!resourceFile.exists()) {
- throw new IllegalArgumentException(String.format("Configuration file at %s does not exist"
- , configurationFile));
+ throw new IllegalArgumentException(
+ String.format("Configuration file at %s does not exist"
+ , configurationFile));
}
return resourceFile;
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
index c1d72ee532..f121767cae 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
@@ -17,22 +17,22 @@
/**
* Description of the modifications:
- *
+ *
* 1) Changed the method signature to adopt the gremlin-server 3.5.1.
* public Optional> selectOther(RequestMessage requestMessage)
* -->
* public Optional> selectOther(Context ctx)
- *
+ *
* 2) Changed the package name.
* org.opencypher.gremlin.server.op.cypher
* -->
* org.apache.hugegraph.api.cypher
- *
*/
package org.apache.hugegraph.api.cypher;
import io.netty.channel.ChannelHandlerContext;
+
import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -59,6 +59,7 @@
import org.opencypher.gremlin.traversal.ProcedureContext;
import org.opencypher.gremlin.traversal.ReturnNormalizer;
import org.slf4j.Logger;
+
import scala.collection.Seq;
import java.util.*;
@@ -123,16 +124,19 @@ private void evalCypher(Context context) throws OpProcessorException {
String translatorDefinition = getTranslatorDefinition(context);
- Translator stringTranslator = Translator.builder()
- .gremlinGroovy()
- .build(translatorDefinition);
+ Translator stringTranslator =
+ Translator.builder()
+ .gremlinGroovy()
+ .build(translatorDefinition);
- Translator traversalTranslator = Translator.builder()
- .traversal(g)
- .build(translatorDefinition);
+ Translator traversalTranslator =
+ Translator.builder()
+ .traversal(g)
+ .build(translatorDefinition);
Seq ir = ast.translate(stringTranslator.flavor(),
- stringTranslator.features(), procedureContext);
+ stringTranslator.features(),
+ procedureContext);
String gremlin = TranslationWriter.write(ir, stringTranslator, parameters);
logger.info("Gremlin: {}", gremlin);
@@ -142,7 +146,8 @@ private void evalCypher(Context context) throws OpProcessorException {
return;
}
- GraphTraversal, ?> traversal = TranslationWriter.write(ir, traversalTranslator, parameters);
+ GraphTraversal, ?> traversal =
+ TranslationWriter.write(ir, traversalTranslator, parameters);
ReturnNormalizer returnNormalizer = ReturnNormalizer.create(ast.getReturnTypes());
Iterator normalizedTraversal = returnNormalizer.normalize(traversal);
inTransaction(gts, () -> handleIterator(context, normalizedTraversal));
@@ -179,11 +184,13 @@ private GraphTraversalSource traversal(Context context) throws OpProcessorExcept
if (gAlias == null) {
return graphManager.getGraphNames().stream()
- .sorted()
- .findFirst()
- .map(graphManager::getGraph)
- .map(Graph::traversal)
- .orElseThrow(() -> opProcessorException(msg, "No graphs found on the server"));
+ .sorted()
+ .findFirst()
+ .map(graphManager::getGraph)
+ .map(Graph::traversal)
+ .orElseThrow(() -> opProcessorException(msg,
+ "No graphs found on the " +
+ "server"));
}
Graph graph = graphManager.getGraph(gAlias);
@@ -201,16 +208,16 @@ private GraphTraversalSource traversal(Context context) throws OpProcessorExcept
private OpProcessorException opProcessorException(RequestMessage msg, String errorMessage) {
return new OpProcessorException(errorMessage,
- ResponseMessage.build(msg)
- .code(SERVER_ERROR)
- .statusMessage(errorMessage).create());
+ ResponseMessage.build(msg)
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage).create());
}
protected void handleIterator(Context context, Iterator traversal) {
RequestMessage msg = context.getRequestMessage();
final long timeout = msg.getArgs().containsKey(Tokens.ARGS_EVAL_TIMEOUT)
- ? ((Number) msg.getArgs().get(Tokens.ARGS_EVAL_TIMEOUT)).longValue()
- : context.getSettings().evaluationTimeout;
+ ? ((Number) msg.getArgs().get(Tokens.ARGS_EVAL_TIMEOUT)).longValue()
+ : context.getSettings().evaluationTimeout;
FutureTask evalFuture = new FutureTask<>(() -> {
try {
@@ -221,17 +228,17 @@ protected void handleIterator(Context context, Iterator traversal) {
logger.error("Error during traversal iteration", ex);
ChannelHandlerContext ctx = context.getChannelHandlerContext();
ctx.writeAndFlush(ResponseMessage.build(msg)
- .code(SERVER_ERROR)
- .statusMessage(errorMessage)
- .statusAttributeException(ex)
- .create());
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex)
+ .create());
}
return null;
}
);
final Future> executionFuture = context.getGremlinExecutor()
- .getExecutorService().submit(evalFuture);
+ .getExecutorService().submit(evalFuture);
if (timeout > 0) {
context.getScheduledExecutorService().schedule(
() -> executionFuture.cancel(true)
@@ -243,7 +250,8 @@ protected void handleIterator(Context context, Iterator traversal) {
private String getErrorMessage(RequestMessage msg, Exception ex) {
if (ex instanceof InterruptedException || ex instanceof TraversalInterruptedException) {
return String.format("A timeout occurred during traversal evaluation of [%s] " +
- "- consider increasing the limit given to scriptEvaluationTimeout", msg);
+ "- consider increasing the limit given to scriptEvaluationTimeout",
+ msg);
} else {
return ex.getMessage();
}
@@ -255,10 +263,10 @@ private void explainQuery(Context context, CypherAst ast, String gremlin) {
explanation.put("options", ast.getOptions().toString());
ResponseMessage explainMsg = ResponseMessage.build(context.getRequestMessage())
- .code(ResponseStatusCode.SUCCESS)
- .statusMessage("OK")
- .result(singletonList(explanation))
- .create();
+ .code(ResponseStatusCode.SUCCESS)
+ .statusMessage("OK")
+ .result(singletonList(explanation))
+ .create();
ChannelHandlerContext ctx = context.getChannelHandlerContext();
ctx.writeAndFlush(explainMsg);
@@ -280,20 +288,20 @@ private Map getParameters(Map args) {
private String getTranslatorDefinition(Context context) {
Map config = context.getSettings()
- .optionalProcessor(CypherOpProcessor.class)
- .map(p -> p.config)
- .orElse(emptyMap());
+ .optionalProcessor(CypherOpProcessor.class)
+ .map(p -> p.config)
+ .orElse(emptyMap());
HashSet properties = new HashSet<>(config.keySet());
properties.remove("translatorDefinition");
properties.remove("translatorFeatures");
if (!properties.isEmpty()) {
throw new IllegalStateException("Unknown configuration parameters " +
- "found for CypherOpProcessor: " + properties);
+ "found for CypherOpProcessor: " + properties);
}
return config.getOrDefault("translatorDefinition", DEFAULT_TRANSLATOR_DEFINITION)
- + "+" + config.getOrDefault("translatorFeatures", "");
+ + "+" + config.getOrDefault("translatorFeatures", "");
}
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
index ea06fb0361..a00369abc9 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
@@ -17,12 +17,11 @@
/**
* Description of the modifications:
- *
+ *
* 1) Changed the package name.
* org.opencypher.gremlin.server.jsr223
* -->
* org.apache.hugegraph.api.cypher
- *
*/
package org.apache.hugegraph.api.cypher;
@@ -43,18 +42,19 @@
public class CypherPlugin implements GremlinPlugin {
- private static final ImportCustomizer imports = DefaultImportCustomizer.build()
- .addClassImports(CustomPredicate.class)
- .addMethodImports(getDeclaredPublicMethods(CustomPredicate.class))
- .addClassImports(CustomFunctions.class)
- .addMethodImports(getDeclaredPublicMethods(CustomFunctions.class))
- .create();
+ private static final ImportCustomizer imports =
+ DefaultImportCustomizer.build()
+ .addClassImports(CustomPredicate.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomPredicate.class))
+ .addClassImports(CustomFunctions.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomFunctions.class))
+ .create();
private static List getDeclaredPublicMethods(Class> klass) {
Method[] declaredMethods = klass.getDeclaredMethods();
return Stream.of(declaredMethods)
- .filter(method -> Modifier.isPublic(method.getModifiers()))
- .collect(Collectors.toList());
+ .filter(method -> Modifier.isPublic(method.getModifiers()))
+ .collect(Collectors.toList());
}
@Override
diff --git a/hugegraph-test/src/main/java/org/apache/hugegraph/api/ApiTestSuite.java b/hugegraph-test/src/main/java/org/apache/hugegraph/api/ApiTestSuite.java
index f3e0378224..a5830a4336 100644
--- a/hugegraph-test/src/main/java/org/apache/hugegraph/api/ApiTestSuite.java
+++ b/hugegraph-test/src/main/java/org/apache/hugegraph/api/ApiTestSuite.java
@@ -39,7 +39,8 @@
UserApiTest.class,
LoginApiTest.class,
ProjectApiTest.class,
- TraversersApiTestSuite.class
+ TraversersApiTestSuite.class,
+ CypherApiTest.class
})
public class ApiTestSuite {
From 92378680dc17a26451b975a4fcf2d4f419f0e30b Mon Sep 17 00:00:00 2001
From: imbajin
Date: Tue, 7 Mar 2023 14:51:55 +0800
Subject: [PATCH 05/11] clean code
---
.../hugegraph/api/cypher/CypherAPI.java | 56 ++++++------
.../hugegraph/api/cypher/CypherClient.java | 53 +++++++-----
.../hugegraph/api/cypher/CypherManager.java | 33 +++----
.../hugegraph/api/cypher/CypherModel.java | 4 +-
.../api/cypher/CypherOpProcessor.java | 85 ++++++++++---------
.../hugegraph/api/cypher/CypherPlugin.java | 18 ++--
6 files changed, 129 insertions(+), 120 deletions(-)
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
index 4ac6f3a6e4..f7c5f6b8ef 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
@@ -17,39 +17,39 @@
package org.apache.hugegraph.api.cypher;
-import jakarta.inject.Singleton;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.hugegraph.HugeException;
import org.apache.hugegraph.api.API;
import org.apache.hugegraph.api.filter.CompressInterceptor;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.Log;
-import com.codahale.metrics.annotation.Timed;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
+import com.codahale.metrics.annotation.Timed;
+
+import jakarta.inject.Singleton;
import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
-import java.util.HashMap;
-import java.util.Map;
-
@Path("graphs/{graph}/cypher")
@Singleton
public class CypherAPI extends API {
private static final Logger LOG = Log.logger(CypherAPI.class);
- private static final Charset UTF8 = Charset.forName(StandardCharsets.UTF_8.name());
+ private static final Charset UTF8 = StandardCharsets.UTF_8;
private final Base64.Decoder decoder = Base64.getUrlDecoder();
private final String basic = "Basic ";
private final String bearer = "Bearer ";
@@ -68,10 +68,8 @@ private CypherManager cypherManager() {
@CompressInterceptor.Compress(buffer = (1024 * 40))
@Produces(APPLICATION_JSON_WITH_CHARSET)
public CypherModel query(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- @QueryParam("cypher") String cypher) {
+ @Context HttpHeaders headers, @QueryParam("cypher") String cypher) {
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
-
return this.queryByCypher(graph, headers, cypher);
}
@@ -81,20 +79,16 @@ public CypherModel query(@PathParam("graph") String graph,
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
public CypherModel post(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- String cypher) {
+ @Context HttpHeaders headers, String cypher) {
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
return this.queryByCypher(graph, headers, cypher);
}
- private CypherModel queryByCypher(String graph,
- HttpHeaders headers,
- String cypher) {
-
+ private CypherModel queryByCypher(String graph, HttpHeaders headers, String cypher) {
E.checkArgument(graph != null && !graph.isEmpty(),
- "The graph parameter can't be null or empty");
+ "The graph parameter can't be null or empty");
E.checkArgument(cypher != null && !cypher.isEmpty(),
- "The cypher parameter can't be null or empty");
+ "The cypher parameter can't be null or empty");
Map aliases = new HashMap<>(1, 1);
aliases.put("g", "__g_" + graph);
@@ -134,11 +128,14 @@ private CypherClient clientViaToken(String auth) {
}
private Pair toUserPass(String auth) {
- if (auth == null || auth.isEmpty()) return null;
- if (!auth.startsWith(basic)) return null;
-
- String[] split = null;
+ if (auth == null || auth.isEmpty()) {
+ return null;
+ }
+ if (!auth.startsWith(basic)) {
+ return null;
+ }
+ String[] split;
try {
String encoded = auth.substring(basic.length());
byte[] userPass = this.decoder.decode(encoded);
@@ -152,7 +149,6 @@ private Pair toUserPass(String auth) {
if (split.length != 2) {
return null;
}
-
return ImmutablePair.of(split[0], split[1]);
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
index e282cf9940..9424fcf4f1 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -26,17 +26,23 @@
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
-import java.util.*;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@ThreadSafe
public final class CypherClient {
+
private static final Logger LOG = Log.logger(CypherClient.class);
+ private final Supplier configurationSupplier;
private String userName;
private String password;
private String token;
- private Supplier configurationSupplier;
CypherClient(String userName, String password,
Supplier configurationSupplier) {
@@ -50,9 +56,9 @@ public final class CypherClient {
this.configurationSupplier = configurationSupplier;
}
- public CypherModel submitQuery(String cypherQuery,@Nullable Map aliases) {
+ public CypherModel submitQuery(String cypherQuery, @Nullable Map aliases) {
E.checkArgument(cypherQuery != null && !cypherQuery.isEmpty(),
- "The cypher-query parameter can't be null or empty");
+ "The cypher-query parameter can't be null or empty");
Cluster cluster = Cluster.open(getConfig());
Client client = cluster.connect();
@@ -62,14 +68,14 @@ public CypherModel submitQuery(String cypherQuery,@Nullable Map
}
RequestMessage request = createRequest(cypherQuery);
- CypherModel res = null;
+ CypherModel res;
try {
List list = this.doQueryList(client, request);
res = CypherModel.dataOf(request.getRequestId().toString(), list);
} catch (Exception e) {
- LOG.error(String.format("Failed to submit cypher-query: [ %s ], cause by:"
- , cypherQuery), e);
+ LOG.error(String.format("Failed to submit cypher-query: [ %s ], cause by:",
+ cypherQuery), e);
res = CypherModel.failOf(request.getRequestId().toString(), e.getMessage());
} finally {
client.close();
@@ -81,21 +87,21 @@ public CypherModel submitQuery(String cypherQuery,@Nullable Map
private RequestMessage createRequest(String cypherQuery) {
return RequestMessage.build(Tokens.OPS_EVAL)
- .processor("cypher")
- .add(Tokens.ARGS_GREMLIN, cypherQuery)
- .create();
+ .processor("cypher")
+ .add(Tokens.ARGS_GREMLIN, cypherQuery)
+ .create();
}
private List doQueryList(Client client, RequestMessage request)
- throws ExecutionException, InterruptedException {
+ throws ExecutionException, InterruptedException {
- ResultSet results = null;
+ ResultSet results;
results = client.submitAsync(request).get();
Iterator iter = results.iterator();
List list = new LinkedList<>();
- for (; iter.hasNext(); ) {
+ while (iter.hasNext()) {
Result data = iter.next();
list.add(data.getObject());
}
@@ -120,13 +126,17 @@ private Configuration getConfig() {
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
CypherClient that = (CypherClient) o;
- return Objects.equals(userName, that.userName)
- && Objects.equals(password, that.password)
- && Objects.equals(token, that.token);
+ return Objects.equals(userName, that.userName) &&
+ Objects.equals(password, that.password) &&
+ Objects.equals(token, that.token);
}
@Override
@@ -136,10 +146,9 @@ public int hashCode() {
@Override
public String toString() {
- final StringBuffer sb = new StringBuffer("CypherClient{");
- sb.append("userName='").append(userName).append('\'');
- sb.append(", token='").append(token).append('\'');
- sb.append('}');
+ final StringBuilder sb = new StringBuilder("CypherClient{");
+ sb.append("userName='").append(userName).append('\'')
+ .append(", token='").append(token).append('\'').append('}');
return sb.toString();
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
index 173cf1d939..0adff82c00 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -17,25 +17,25 @@
package org.apache.hugegraph.api.cypher;
-import org.apache.hugegraph.util.E;
-import org.apache.commons.configuration2.Configuration;
-import org.apache.commons.configuration2.YAMLConfiguration;
-
-import javax.annotation.concurrent.ThreadSafe;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
-
import java.net.URL;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.YAMLConfiguration;
+import org.apache.hugegraph.util.E;
+
@ThreadSafe
public final class CypherManager {
- private String configurationFile;
+ private final String configurationFile;
private YAMLConfiguration configuration;
public static CypherManager configOf(String configurationFile) {
E.checkArgument(configurationFile != null && !configurationFile.isEmpty(),
- "The configurationFile parameter can't be null or empty");
+ "The configurationFile parameter can't be null or empty");
return new CypherManager(configurationFile);
}
@@ -45,20 +45,20 @@ private CypherManager(String configurationFile) {
public CypherClient getClient(String userName, String password) {
E.checkArgument(userName != null && !userName.isEmpty(),
- "The userName parameter can't be null or empty");
+ "The userName parameter can't be null or empty");
E.checkArgument(password != null && !password.isEmpty(),
- "The password parameter can't be null or empty");
+ "The password parameter can't be null or empty");
//TODO: Need to cache the client and make it hold the connection.
- return new CypherClient(userName, password, () -> this.cloneConfig());
+ return new CypherClient(userName, password, this::cloneConfig);
}
public CypherClient getClient(String token) {
E.checkArgument(token != null && !token.isEmpty(),
- "The token parameter can't be null or empty");
+ "The token parameter can't be null or empty");
//TODO: Need to cache the client and make it hold the connection.
- return new CypherClient(token, () -> this.cloneConfig());
+ return new CypherClient(token, this::cloneConfig);
}
private Configuration cloneConfig() {
@@ -79,7 +79,7 @@ private static YAMLConfiguration loadYaml(String configurationFile) {
yaml.read(reader);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to load configuration file," +
- " the file at %s.", configurationFile), e);
+ " the file at %s.", configurationFile), e);
}
return yaml;
@@ -91,11 +91,12 @@ private static File getConfigFile(String configurationFile) {
if (!systemFile.exists()) {
final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
final URL resource = currentClassLoader.getResource(configurationFile);
+ assert resource != null;
final File resourceFile = new File(resource.getFile());
if (!resourceFile.exists()) {
- throw new IllegalArgumentException(String.format("Configuration file at %s does not exist"
- , configurationFile));
+ throw new IllegalArgumentException(String.format("Configuration file at %s does " +
+ "not exist", configurationFile));
}
return resourceFile;
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
index 31230248ef..7a259e48e4 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
@@ -48,13 +48,13 @@ public static CypherModel failOf(String requestId, String message) {
private CypherModel() {
}
- public class Status {
+ public static class Status {
public String message = "";
public int code;
public Map attributes = Collections.EMPTY_MAP;
}
- private class Result {
+ private static class Result {
public List data;
public Map meta = Collections.EMPTY_MAP;
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
index 99bbb47bb6..c95b6dc3d9 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.api.cypher;
import io.netty.channel.ChannelHandlerContext;
+
import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -43,6 +44,7 @@
import org.opencypher.gremlin.traversal.ProcedureContext;
import org.opencypher.gremlin.traversal.ReturnNormalizer;
import org.slf4j.Logger;
+
import scala.collection.Seq;
import java.util.*;
@@ -89,8 +91,7 @@ public ThrowingConsumer getEvalOp() {
}
@Override
- public Optional> selectOther(Context ctx)
- throws OpProcessorException {
+ public Optional> selectOther(Context ctx) {
return empty();
}
@@ -107,18 +108,18 @@ private void evalCypher(Context context) throws OpProcessorException {
String translatorDefinition = getTranslatorDefinition(context);
- Translator stringTranslator = Translator.builder()
- .gremlinGroovy()
- .build(translatorDefinition);
+ Translator strTranslator = Translator.builder()
+ .gremlinGroovy()
+ .build(translatorDefinition);
Translator traversalTranslator = Translator.builder()
- .traversal(g)
- .build(translatorDefinition);
+ .traversal(g)
+ .build(translatorDefinition);
- Seq ir = ast.translate(stringTranslator.flavor(),
- stringTranslator.features(), procedureContext);
+ Seq ir = ast.translate(strTranslator.flavor(),
+ strTranslator.features(), procedureContext);
- String gremlin = TranslationWriter.write(ir, stringTranslator, parameters);
+ String gremlin = TranslationWriter.write(ir, strTranslator, parameters);
logger.info("Gremlin: {}", gremlin);
if (ast.getOptions().contains(EXPLAIN)) {
@@ -126,7 +127,8 @@ private void evalCypher(Context context) throws OpProcessorException {
return;
}
- GraphTraversal, ?> traversal = TranslationWriter.write(ir, traversalTranslator, parameters);
+ GraphTraversal, ?> traversal = TranslationWriter.write(ir, traversalTranslator,
+ parameters);
ReturnNormalizer returnNormalizer = ReturnNormalizer.create(ast.getReturnTypes());
Iterator normalizedTraversal = returnNormalizer.normalize(traversal);
inTransaction(gts, () -> handleIterator(context, normalizedTraversal));
@@ -135,7 +137,6 @@ private void evalCypher(Context context) throws OpProcessorException {
private void inTransaction(GraphTraversalSource gts, Runnable runnable) {
Graph graph = gts.getGraph();
boolean supportsTransactions = graph.features().graph().supportsTransactions();
-
if (!supportsTransactions) {
runnable.run();
return;
@@ -157,17 +158,17 @@ private GraphTraversalSource traversal(Context context) throws OpProcessorExcept
GraphManager graphManager = context.getGraphManager();
Optional> aliasesOptional = msg.optionalArgs(Tokens.ARGS_ALIASES);
- String gAlias = aliasesOptional
- .map(aliases -> aliases.get(Tokens.VAL_TRAVERSAL_SOURCE_ALIAS))
- .orElse(null);
+ String gAlias = aliasesOptional.map(alias -> alias.get(Tokens.VAL_TRAVERSAL_SOURCE_ALIAS))
+ .orElse(null);
if (gAlias == null) {
return graphManager.getGraphNames().stream()
- .sorted()
- .findFirst()
- .map(graphManager::getGraph)
- .map(Graph::traversal)
- .orElseThrow(() -> opProcessorException(msg, "No graphs found on the server"));
+ .sorted()
+ .findFirst()
+ .map(graphManager::getGraph)
+ .map(Graph::traversal)
+ .orElseThrow(() -> opProcessorException(msg, "No graphs found on " +
+ "the server"));
}
Graph graph = graphManager.getGraph(gAlias);
@@ -184,17 +185,18 @@ private GraphTraversalSource traversal(Context context) throws OpProcessorExcept
}
private OpProcessorException opProcessorException(RequestMessage msg, String errorMessage) {
- return new OpProcessorException(errorMessage,
- ResponseMessage.build(msg)
- .code(SERVER_ERROR)
- .statusMessage(errorMessage).create());
+ return new OpProcessorException(errorMessage, ResponseMessage.build(msg)
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage)
+ .create());
}
+ @Override
protected void handleIterator(Context context, Iterator traversal) {
RequestMessage msg = context.getRequestMessage();
final long timeout = msg.getArgs().containsKey(Tokens.ARGS_EVAL_TIMEOUT)
- ? ((Number) msg.getArgs().get(Tokens.ARGS_EVAL_TIMEOUT)).longValue()
- : context.getSettings().evaluationTimeout;
+ ? ((Number) msg.getArgs().get(Tokens.ARGS_EVAL_TIMEOUT)).longValue()
+ : context.getSettings().evaluationTimeout;
FutureTask evalFuture = new FutureTask<>(() -> {
try {
@@ -205,17 +207,17 @@ protected void handleIterator(Context context, Iterator traversal) {
logger.error("Error during traversal iteration", ex);
ChannelHandlerContext ctx = context.getChannelHandlerContext();
ctx.writeAndFlush(ResponseMessage.build(msg)
- .code(SERVER_ERROR)
- .statusMessage(errorMessage)
- .statusAttributeException(ex)
- .create());
+ .code(SERVER_ERROR)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex)
+ .create());
}
return null;
}
);
final Future> executionFuture = context.getGremlinExecutor()
- .getExecutorService().submit(evalFuture);
+ .getExecutorService().submit(evalFuture);
if (timeout > 0) {
context.getScheduledExecutorService().schedule(
() -> executionFuture.cancel(true)
@@ -227,7 +229,8 @@ protected void handleIterator(Context context, Iterator traversal) {
private String getErrorMessage(RequestMessage msg, Exception ex) {
if (ex instanceof InterruptedException || ex instanceof TraversalInterruptedException) {
return String.format("A timeout occurred during traversal evaluation of [%s] " +
- "- consider increasing the limit given to scriptEvaluationTimeout", msg);
+ "- consider increasing the limit given to scriptEvaluationTimeout",
+ msg);
} else {
return ex.getMessage();
}
@@ -239,10 +242,10 @@ private void explainQuery(Context context, CypherAst ast, String gremlin) {
explanation.put("options", ast.getOptions().toString());
ResponseMessage explainMsg = ResponseMessage.build(context.getRequestMessage())
- .code(ResponseStatusCode.SUCCESS)
- .statusMessage("OK")
- .result(singletonList(explanation))
- .create();
+ .code(ResponseStatusCode.SUCCESS)
+ .statusMessage("OK")
+ .result(singletonList(explanation))
+ .create();
ChannelHandlerContext ctx = context.getChannelHandlerContext();
ctx.writeAndFlush(explainMsg);
@@ -264,20 +267,20 @@ private Map getParameters(Map args) {
private String getTranslatorDefinition(Context context) {
Map config = context.getSettings()
- .optionalProcessor(CypherOpProcessor.class)
- .map(p -> p.config)
- .orElse(emptyMap());
+ .optionalProcessor(CypherOpProcessor.class)
+ .map(p -> p.config)
+ .orElse(emptyMap());
HashSet properties = new HashSet<>(config.keySet());
properties.remove("translatorDefinition");
properties.remove("translatorFeatures");
if (!properties.isEmpty()) {
throw new IllegalStateException("Unknown configuration parameters " +
- "found for CypherOpProcessor: " + properties);
+ "found for CypherOpProcessor: " + properties);
}
return config.getOrDefault("translatorDefinition", DEFAULT_TRANSLATOR_DEFINITION)
- + "+" + config.getOrDefault("translatorFeatures", "");
+ + "+" + config.getOrDefault("translatorFeatures", "");
}
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
index fc6c2dda6e..fce1a602ab 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
@@ -32,18 +32,18 @@
public class CypherPlugin implements GremlinPlugin {
- private static final ImportCustomizer imports = DefaultImportCustomizer.build()
- .addClassImports(CustomPredicate.class)
- .addMethodImports(getDeclaredPublicMethods(CustomPredicate.class))
- .addClassImports(CustomFunctions.class)
- .addMethodImports(getDeclaredPublicMethods(CustomFunctions.class))
- .create();
+ private static final ImportCustomizer IMPORTS =
+ DefaultImportCustomizer.build().addClassImports(CustomPredicate.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomPredicate.class))
+ .addClassImports(CustomFunctions.class)
+ .addMethodImports(getDeclaredPublicMethods(CustomFunctions.class))
+ .create();
private static List getDeclaredPublicMethods(Class> klass) {
Method[] declaredMethods = klass.getDeclaredMethods();
return Stream.of(declaredMethods)
- .filter(method -> Modifier.isPublic(method.getModifiers()))
- .collect(Collectors.toList());
+ .filter(method -> Modifier.isPublic(method.getModifiers()))
+ .collect(Collectors.toList());
}
@Override
@@ -62,6 +62,6 @@ public boolean requireRestart() {
@Override
public Optional getCustomizers(String scriptEngineName) {
- return Optional.of(new Customizer[]{imports});
+ return Optional.of(new Customizer[]{IMPORTS});
}
}
From b3ae0bfa8fb24e8c9dae50c8a9861616d01e8e6f Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Tue, 7 Mar 2023 14:53:24 +0800
Subject: [PATCH 06/11] Made it follow the HugeGraph style
---
LICENSE | 4 ++--
.../hugegraph/api/cypher/CypherAPI.java | 23 ++++++++++---------
.../hugegraph/api/cypher/CypherClient.java | 10 +++++---
.../hugegraph/api/cypher/CypherManager.java | 2 +-
.../hugegraph/api/cypher/CypherModel.java | 1 +
.../CypherOpProcessor.java | 19 +++++++++++----
.../cypher => opencypher}/CypherPlugin.java | 7 ++++--
...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 2 +-
...pache.tinkerpop.gremlin.server.OpProcessor | 2 +-
.../optimize/HugePrimaryKeyStrategy.java | 2 --
10 files changed, 44 insertions(+), 28 deletions(-)
rename hugegraph-api/src/main/java/org/apache/hugegraph/{api/cypher => opencypher}/CypherOpProcessor.java (97%)
rename hugegraph-api/src/main/java/org/apache/hugegraph/{api/cypher => opencypher}/CypherPlugin.java (96%)
diff --git a/LICENSE b/LICENSE
index 0634009f19..ad08080e31 100644
--- a/LICENSE
+++ b/LICENSE
@@ -214,5 +214,5 @@ hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugeScriptT
hugegraph-core/src/main/java/org/apache/hugegraph/type/Nameable.java from https://github.com/JanusGraph/janusgraph
hugegraph-core/src/main/java/org/apache/hugegraph/type/define/Cardinality.java from https://github.com/JanusGraph/janusgraph
hugegraph-core/src/main/java/org/apache/hugegraph/util/StringEncoding.java from https://github.com/JanusGraph/janusgraph
-hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java from https://github.com/opencypher/cypher-for-gremlin
-hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java from https://github.com/opencypher/cypher-for-gremlin
+hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java from https://github.com/opencypher/cypher-for-gremlin
+hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java from https://github.com/opencypher/cypher-for-gremlin
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
index b68df4d97f..ca9d324372 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherAPI.java
@@ -19,6 +19,7 @@
import jakarta.inject.Singleton;
import jakarta.ws.rs.GET;
+import jakarta.ws.rs.NotAuthorizedException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@@ -50,8 +51,10 @@
@Path("graphs/{graph}/cypher")
@Singleton
public class CypherAPI extends API {
+
private static final Logger LOG = Log.logger(CypherAPI.class);
private static final Charset UTF8 = Charset.forName(StandardCharsets.UTF_8.name());
+ private static final String CLIENT_CONF = "conf/remote-objects.yaml";
private final Base64.Decoder decoder = Base64.getUrlDecoder();
private final String basic = "Basic ";
private final String bearer = "Bearer ";
@@ -60,7 +63,7 @@ public class CypherAPI extends API {
private CypherManager cypherManager() {
if (this.cypherManager == null) {
- this.cypherManager = CypherManager.configOf("conf/remote-objects.yaml");
+ this.cypherManager = CypherManager.configOf(CLIENT_CONF);
}
return this.cypherManager;
}
@@ -107,17 +110,16 @@ private CypherClient client(HttpHeaders headers) {
auth = auth.split(",")[0];
}
- if (auth == null) {
- throw new HugeException("The Cypher-API is being called without any authorization.");
- }
-
- if (auth.startsWith(basic)) {
- return this.clientViaBasic(auth);
- } else if (auth.startsWith(bearer)) {
- return this.clientViaToken(auth);
+ if (auth != null) {
+ if (auth.startsWith(basic)) {
+ return this.clientViaBasic(auth);
+ } else if (auth.startsWith(bearer)) {
+ return this.clientViaToken(auth);
+ }
}
- throw new HugeException("The Cypher-API is being called without any authorization.");
+ throw new NotAuthorizedException(
+ "The Cypher-API is being called without any authorization.");
}
private CypherClient clientViaBasic(String auth) {
@@ -153,5 +155,4 @@ private Pair toUserPass(String auth) {
return ImmutablePair.of(split[0], split[1]);
}
-
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
index 8669717bc3..5f082b5c41 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -27,12 +27,17 @@
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
-import java.util.*;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@ThreadSafe
public final class CypherClient {
+
private static final Logger LOG = Log.logger(CypherClient.class);
private String userName;
private String password;
@@ -90,8 +95,7 @@ private RequestMessage createRequest(String cypherQuery) {
private List doQueryList(Client client, RequestMessage request)
throws ExecutionException, InterruptedException {
- ResultSet results = null;
- results = client.submitAsync(request).get();
+ ResultSet results = client.submitAsync(request).get();
Iterator iter = results.iterator();
List list = new LinkedList<>();
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
index 69c43a3f0b..bc4df43dc1 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -31,6 +31,7 @@
@ThreadSafe
public final class CypherManager {
+
private String configurationFile;
private YAMLConfiguration configuration;
@@ -105,5 +106,4 @@ private static File getConfigFile(String configurationFile) {
return systemFile;
}
-
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
index 31230248ef..19c9e5245c 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherModel.java
@@ -25,6 +25,7 @@
* As same as response of GremlinAPI
*/
public class CypherModel {
+
public String requestId;
public Status status = new Status();
public Result result = new Result();
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
similarity index 97%
rename from hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
rename to hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
index f121767cae..18798fbeff 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
@@ -14,22 +14,30 @@
* limitations under the License.
*/
-
/**
* Description of the modifications:
*
* 1) Changed the method signature to adopt the gremlin-server 3.5.1.
+ *
* public Optional> selectOther(RequestMessage requestMessage)
* -->
* public Optional> selectOther(Context ctx)
+ *
+ *
*
* 2) Changed the package name.
+ *
* org.opencypher.gremlin.server.op.cypher
* -->
- * org.apache.hugegraph.api.cypher
+ * org.apache.hugegraph.opencypher
+ *
+ *
+ *
+ * 3) Set the logger level from info to trace
+ *
*/
-package org.apache.hugegraph.api.cypher;
+package org.apache.hugegraph.opencypher;
import io.netty.channel.ChannelHandlerContext;
@@ -86,6 +94,7 @@
*
*/
public class CypherOpProcessor extends AbstractEvalOpProcessor {
+
private static final String DEFAULT_TRANSLATOR_DEFINITION =
"gremlin+cfog_server_extensions+inline_parameters";
@@ -114,7 +123,7 @@ public Optional> selectOther(Context ctx)
private void evalCypher(Context context) throws OpProcessorException {
Map args = context.getRequestMessage().getArgs();
String cypher = (String) args.get(Tokens.ARGS_GREMLIN);
- logger.info("Cypher: {}", cypher.replaceAll("\n", " "));
+ logger.trace("Cypher: {}", cypher.replaceAll("\n", " "));
GraphTraversalSource gts = traversal(context);
DefaultGraphTraversal g = new DefaultGraphTraversal(gts.clone());
@@ -139,7 +148,7 @@ private void evalCypher(Context context) throws OpProcessorException {
procedureContext);
String gremlin = TranslationWriter.write(ir, stringTranslator, parameters);
- logger.info("Gremlin: {}", gremlin);
+ logger.trace("Gremlin: {}", gremlin);
if (ast.getOptions().contains(EXPLAIN)) {
explainQuery(context, ast, gremlin);
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
similarity index 96%
rename from hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
rename to hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
index a00369abc9..50a295008a 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
@@ -19,12 +19,15 @@
* Description of the modifications:
*
* 1) Changed the package name.
+ *
* org.opencypher.gremlin.server.jsr223
* -->
- * org.apache.hugegraph.api.cypher
+ * org.apache.hugegraph.opencypher
+ *
+ *
*/
-package org.apache.hugegraph.api.cypher;
+package org.apache.hugegraph.opencypher;
import org.apache.tinkerpop.gremlin.jsr223.Customizer;
import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
diff --git a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
index be2a5bfad4..4173c7e065 100644
--- a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
+++ b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
@@ -1 +1 @@
-org.apache.hugegraph.api.cypher.CypherPlugin
+org.apache.hugegraph.opencypher.CypherPlugin
diff --git a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
index 58b2be3401..84b1028e9b 100644
--- a/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
+++ b/hugegraph-api/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.server.OpProcessor
@@ -1 +1 @@
-org.apache.hugegraph.api.cypher.CypherOpProcessor
+org.apache.hugegraph.opencypher.CypherOpProcessor
diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java b/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
index 1dcffe9b07..e00e4caf80 100644
--- a/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
+++ b/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugePrimaryKeyStrategy.java
@@ -32,7 +32,6 @@
import java.util.LinkedList;
import java.util.List;
-
public class HugePrimaryKeyStrategy
extends AbstractTraversalStrategy
implements ProviderOptimizationStrategy {
@@ -104,6 +103,5 @@ public void apply(Traversal.Admin, ?> traversal) {
for (Step index : removeSteps) {
traversal.removeStep(index);
}
-
}
}
From c049f6bf043f35462c2a4389f6a52a1d7780fb5f Mon Sep 17 00:00:00 2001
From: imbajin
Date: Tue, 7 Mar 2023 15:08:53 +0800
Subject: [PATCH 07/11] fix license
---
.licenserc.yaml | 4 +-
.../opencypher/CypherOpProcessor.java | 44 +++++++++----------
.../hugegraph/opencypher/CypherPlugin.java | 25 +++++------
hugegraph-dist/release-docs/LICENSE | 2 +
4 files changed, 37 insertions(+), 38 deletions(-)
diff --git a/.licenserc.yaml b/.licenserc.yaml
index 6a98da9a5e..334d89b51e 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -94,8 +94,8 @@ header: # `header` section is configurations for source codes license header.
- '**/type/Nameable.java'
- '**/define/Cardinality.java'
- '**/util/StringEncoding.java'
- - 'hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherOpProcessor.java'
- - 'hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherPlugin.java'
+ - 'hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java'
+ - 'hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java'
comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`.
# license-location-threshold specifies the index threshold where the license header can be located,
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
index 071a719b93..a4dfff60a4 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java
@@ -14,29 +14,6 @@
* limitations under the License.
*/
-/**
- * Description of the modifications:
- *
- * 1) Changed the method signature to adopt the gremlin-server 3.5.1.
- *
- * public Optional> selectOther(RequestMessage requestMessage)
- * -->
- * public Optional> selectOther(Context ctx)
- *
- *
- *
- * 2) Changed the package name.
- *
- * org.opencypher.gremlin.server.op.cypher
- * -->
- * org.apache.hugegraph.opencypher
- *
- *
- *
- * 3) Set the logger level from info to trace
- *
- */
-
package org.apache.hugegraph.opencypher;
import io.netty.channel.ChannelHandlerContext;
@@ -83,6 +60,27 @@
import static org.slf4j.LoggerFactory.getLogger;
/**
+ * Description of the modifications:
+ *
+ * 1) Changed the method signature to adopt the gremlin-server 3.5.1.
+ *
+ * public Optional> selectOther(RequestMessage requestMessage)
+ * -->
+ * public Optional> selectOther(Context ctx)
+ *
+ *
+ *
+ * 2) Changed the package name.
+ *
+ * org.opencypher.gremlin.server.op.cypher
+ * -->
+ * org.apache.hugegraph.opencypher
+ *
+ *
+ *
+ * 3) Set the logger level from info to trace
+ *
+ *
* {@link OpProcessor} implementation for processing Cypher {@link RequestMessage}s:
*
* {
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
index b0c4a465fe..a8f43c9099 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
@@ -14,19 +14,6 @@
* limitations under the License.
*/
-
-/**
- * Description of the modifications:
- *
- * 1) Changed the package name.
- *
- * org.opencypher.gremlin.server.jsr223
- * -->
- * org.apache.hugegraph.opencypher
- *
- *
- */
-
package org.apache.hugegraph.opencypher;
import org.apache.tinkerpop.gremlin.jsr223.Customizer;
@@ -43,6 +30,18 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+
+/**
+ * Description of the modifications:
+ *
+ * 1) Changed the package name.
+ *
+ * org.opencypher.gremlin.server.jsr223
+ * -->
+ * org.apache.hugegraph.opencypher
+ *
+ *
+ */
public class CypherPlugin implements GremlinPlugin {
private static final ImportCustomizer IMPORTS =
diff --git a/hugegraph-dist/release-docs/LICENSE b/hugegraph-dist/release-docs/LICENSE
index d9e5fb9fd7..2c2f5f90a9 100644
--- a/hugegraph-dist/release-docs/LICENSE
+++ b/hugegraph-dist/release-docs/LICENSE
@@ -224,6 +224,8 @@ hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugeScriptT
hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/ProcessBasicSuite.java from https://github.com/apache/tinkerpop
hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/StructureBasicSuite.java from https://github.com/apache/tinkerpop
hugegraph-core/src/main/java/org/apache/hugegraph/backend/id/SnowflakeIdGenerator.java from https://github.com/twitter-archive/snowflake
+hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java from https://github.com/opencypher/cypher-for-gremlin
+hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java from https://github.com/opencypher/cypher-for-gremlin
========================================================================
From 4805d317ebcf457348be5760f481ee5374df13a9 Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Tue, 7 Mar 2023 20:08:28 +0800
Subject: [PATCH 08/11] Adjusted some formats.
---
.../hugegraph/api/cypher/CypherClient.java | 2 +-
.../hugegraph/opencypher/CypherPlugin.java | 24 +++++++++----------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
index ac05abdd74..c68f91b866 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -112,7 +112,7 @@ private List doQueryList(Client client, RequestMessage request)
* we had to use a trick to fix it. When the token is set, the password will be set to
* an empty string, which is an uncommon value under normal conditions.
* The token will then be transferred through the userName-property.
- * To see org.apache.hugegraph.auth.StandardAuthenticator.PlainTextSaslAuthenticator
+ * To see org.apache.hugegraph.auth.StandardAuthenticator.TokenSaslAuthenticator
*/
private Configuration getConfig() {
Configuration conf = this.configurationSupplier.get();
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
index a8f43c9099..98cf98eeef 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java
@@ -14,6 +14,18 @@
* limitations under the License.
*/
+/**
+ * Description of the modifications:
+ *
+ * 1) Changed the package name.
+ *
+ * org.opencypher.gremlin.server.jsr223
+ * -->
+ * org.apache.hugegraph.opencypher
+ *
+ *
+ */
+
package org.apache.hugegraph.opencypher;
import org.apache.tinkerpop.gremlin.jsr223.Customizer;
@@ -30,18 +42,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-
-/**
- * Description of the modifications:
- *
- * 1) Changed the package name.
- *
- * org.opencypher.gremlin.server.jsr223
- * -->
- * org.apache.hugegraph.opencypher
- *
- *
- */
public class CypherPlugin implements GremlinPlugin {
private static final ImportCustomizer IMPORTS =
From 4cadd1403d0f82cf8d0bfb3da939904d4b449cbd Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Wed, 8 Mar 2023 11:01:17 +0800
Subject: [PATCH 09/11] Removed the previous CypherAPI and made some formatting
changes.
---
.../hugegraph/api/cypher/CypherClient.java | 10 +-
.../hugegraph/api/cypher/CypherManager.java | 6 +-
.../hugegraph/api/gremlin/CypherAPI.java | 112 ------------------
3 files changed, 8 insertions(+), 120 deletions(-)
delete mode 100644 hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
index c68f91b866..10e92f2c78 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherClient.java
@@ -74,7 +74,7 @@ public CypherModel submitQuery(String cypherQuery, @Nullable Map
List list = this.doQueryList(client, request);
res = CypherModel.dataOf(request.getRequestId().toString(), list);
} catch (Exception e) {
- LOG.error(String.format("Failed to submit cypher-query: [ %s ], cause by:",
+ LOG.error(String.format("Failed to submit cypher-query: [ %s ], caused by:",
cypherQuery), e);
res = CypherModel.failOf(request.getRequestId().toString(), e.getMessage());
} finally {
@@ -144,10 +144,10 @@ public int hashCode() {
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder("CypherClient{");
- sb.append("userName='").append(userName).append('\'')
- .append(", token='").append(token).append('\'').append('}');
+ final StringBuilder builder = new StringBuilder("CypherClient{");
+ builder.append("userName='").append(userName).append('\'')
+ .append(", token='").append(token).append('\'').append('}');
- return sb.toString();
+ return builder.toString();
}
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
index 5bb21fd9b8..c2ef14e5e3 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -79,7 +79,7 @@ private static YAMLConfiguration loadYaml(String configurationFile) {
yaml.read(reader);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to load configuration file," +
- " the file at %s.", configurationFile), e);
+ " the file at '%s'.", configurationFile), e);
}
return yaml;
@@ -95,8 +95,8 @@ private static File getConfigFile(String configurationFile) {
final File resourceFile = new File(resource.getFile());
if (!resourceFile.exists()) {
- throw new IllegalArgumentException(String.format("Configuration file at %s does " +
- "not exist", configurationFile));
+ throw new IllegalArgumentException(String.format("Configuration file at '%s' does" +
+ " not exist", configurationFile));
}
return resourceFile;
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
deleted file mode 100644
index 19802580ae..0000000000
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/gremlin/CypherAPI.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.hugegraph.api.gremlin;
-
-import org.opencypher.gremlin.translation.TranslationFacade;
-import org.slf4j.Logger;
-
-import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
-import org.apache.hugegraph.util.E;
-import org.apache.hugegraph.util.Log;
-import com.codahale.metrics.annotation.Timed;
-
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.inject.Singleton;
-import jakarta.ws.rs.Consumes;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.QueryParam;
-import jakarta.ws.rs.core.Context;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.Response;
-
-//@Path("graphs/{graph}/cypher")
-@Singleton
-@Tag(name = "CypherAPI")
-@Deprecated
-public class CypherAPI extends GremlinQueryAPI {
-
- private static final Logger LOG = Log.logger(CypherAPI.class);
-
- @GET
- @Timed
- @Compress(buffer = (1024 * 40))
- @Produces(APPLICATION_JSON_WITH_CHARSET)
- public Response query(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- @QueryParam("cypher") String cypher) {
- LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
-
- return this.queryByCypher(graph, headers, cypher);
- }
-
- @POST
- @Timed
- @Compress
- @Consumes(APPLICATION_JSON)
- @Produces(APPLICATION_JSON_WITH_CHARSET)
- public Response post(@PathParam("graph") String graph,
- @Context HttpHeaders headers,
- String cypher) {
- LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
- return this.queryByCypher(graph, headers, cypher);
- }
-
- private Response queryByCypher(String graph,
- HttpHeaders headers,
- String cypher) {
- E.checkArgument(cypher != null && !cypher.isEmpty(),
- "The cypher parameter can't be null or empty");
-
- String gremlin = this.translateCpyher2Gremlin(graph, cypher);
- LOG.debug("translated gremlin is {}", gremlin);
-
- String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
- String request = "{" +
- "\"gremlin\":\"" + gremlin + "\"," +
- "\"bindings\":{}," +
- "\"language\":\"gremlin-groovy\"," +
- "\"aliases\":{\"g\":\"__g_" + graph + "\"}}";
-
- Response response = this.client().doPostRequest(auth, request);
- return transformResponseIfNeeded(response);
- }
-
- private String translateCpyher2Gremlin(String graph, String cypher) {
- TranslationFacade translator = new TranslationFacade();
- String gremlin = translator.toGremlinGroovy(cypher);
- gremlin = this.buildQueryableGremlin(graph, gremlin);
- return gremlin;
- }
-
- private String buildQueryableGremlin(String graph, String gremlin) {
- /*
- * `CREATE (a:person { name : 'test', age: 20) return a`
- * would be translated to:
- * `g.addV('person').as('a').property(single, 'name', 'test') ...`,
- * but hugegraph don't support `.property(single, k, v)`,
- * so we replace it to `.property(k, v)` here
- */
- gremlin = gremlin.replace(".property(single,", ".property(");
-
- return gremlin;
- }
-}
From 378293ff7fb98a333d84b793ab1d7e915707afcd Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Tue, 14 Mar 2023 11:51:59 +0800
Subject: [PATCH 10/11] Ajusted some formats
---
.../hugegraph/api/cypher/CypherManager.java | 20 +++++++------------
.../hugegraph/auth/StandardAuthenticator.java | 18 ++++++++---------
2 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
index c2ef14e5e3..519ca66d9e 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/cypher/CypherManager.java
@@ -30,6 +30,7 @@
@ThreadSafe
public final class CypherManager {
+
private final String configurationFile;
private YAMLConfiguration configuration;
@@ -49,7 +50,7 @@ public CypherClient getClient(String userName, String password) {
E.checkArgument(password != null && !password.isEmpty(),
"The password parameter can't be null or empty");
- //TODO: Need to cache the client and make it hold the connection.
+ // TODO: Need to cache the client and make it hold the connection.
return new CypherClient(userName, password, this::cloneConfig);
}
@@ -57,7 +58,7 @@ public CypherClient getClient(String token) {
E.checkArgument(token != null && !token.isEmpty(),
"The token parameter can't be null or empty");
- //TODO: Need to cache the client and make it hold the connection.
+ // TODO: Need to cache the client and make it hold the connection.
return new CypherClient(token, this::cloneConfig);
}
@@ -65,14 +66,12 @@ private Configuration cloneConfig() {
if (this.configuration == null) {
this.configuration = loadYaml(this.configurationFile);
}
-
return (Configuration) this.configuration.clone();
}
private static YAMLConfiguration loadYaml(String configurationFile) {
File yamlFile = getConfigFile(configurationFile);
YAMLConfiguration yaml;
-
try {
Reader reader = new FileReader(yamlFile);
yaml = new YAMLConfiguration();
@@ -81,27 +80,22 @@ private static YAMLConfiguration loadYaml(String configurationFile) {
throw new RuntimeException(String.format("Failed to load configuration file," +
" the file at '%s'.", configurationFile), e);
}
-
return yaml;
}
private static File getConfigFile(String configurationFile) {
- final File systemFile = new File(configurationFile);
-
+ File systemFile = new File(configurationFile);
if (!systemFile.exists()) {
- final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
- final URL resource = currentClassLoader.getResource(configurationFile);
+ ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+ URL resource = currentClassLoader.getResource(configurationFile);
assert resource != null;
- final File resourceFile = new File(resource.getFile());
-
+ File resourceFile = new File(resource.getFile());
if (!resourceFile.exists()) {
throw new IllegalArgumentException(String.format("Configuration file at '%s' does" +
" not exist", configurationFile));
}
return resourceFile;
-
}
-
return systemFile;
}
}
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
index 5fc4f49e4a..e706505b24 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
@@ -216,6 +216,7 @@ public static void initAdminUserIfNeeded(String confFile) throws Exception {
}
private class TokenSaslAuthenticator implements SaslNegotiator {
+
private static final byte NUL = 0;
private String username;
private String password;
@@ -261,16 +262,15 @@ private void decode(byte[] bytes) throws AuthenticationException {
int end = bytes.length;
for (int i = bytes.length - 1; i >= 0; i--) {
- if (bytes[i] == NUL) {
- if (this.password == null) {
- password = new String(Arrays.copyOfRange(bytes, i + 1, end),
- StandardCharsets.UTF_8);
- } else if (this.username == null) {
- username = new String(Arrays.copyOfRange(bytes, i + 1, end),
- StandardCharsets.UTF_8);
- }
- end = i;
+ if (bytes[i] != NUL) continue;
+ if (this.password == null) {
+ password = new String(Arrays.copyOfRange(bytes, i + 1, end),
+ StandardCharsets.UTF_8);
+ } else if (this.username == null) {
+ username = new String(Arrays.copyOfRange(bytes, i + 1, end),
+ StandardCharsets.UTF_8);
}
+ end = i;
}
if (this.username == null) {
From 5457d18d0f8af579cf81ac3bc8ac328928c9300c Mon Sep 17 00:00:00 2001
From: "lynn.bond"
Date: Wed, 15 Mar 2023 10:33:24 +0800
Subject: [PATCH 11/11] Adjusted the if-statement format.
---
.../java/org/apache/hugegraph/auth/StandardAuthenticator.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
index e706505b24..48e74225f0 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
+++ b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java
@@ -262,7 +262,9 @@ private void decode(byte[] bytes) throws AuthenticationException {
int end = bytes.length;
for (int i = bytes.length - 1; i >= 0; i--) {
- if (bytes[i] != NUL) continue;
+ if (bytes[i] != NUL) {
+ continue;
+ }
if (this.password == null) {
password = new String(Arrays.copyOfRange(bytes, i + 1, end),
StandardCharsets.UTF_8);