diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java index e6c7e2d982..92f5e08642 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java @@ -22,20 +22,43 @@ import java.lang.annotation.RetentionPolicy; import java.net.URI; import java.net.URISyntaxException; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import jakarta.ws.rs.NameBinding; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; import org.apache.hugegraph.election.GlobalMasterInfo; import org.apache.hugegraph.util.Log; +import org.glassfish.jersey.message.internal.HeaderUtils; import org.slf4j.Logger; public class RedirectFilter implements ContainerRequestFilter { private static final Logger LOG = Log.logger(RedirectFilter.class); + public static final String X_HG_REDIRECT = "x-hg-redirect"; + + private static volatile Client client = null; + + private static final Set MUST_BE_NULL = new HashSet<>(); + + static { + MUST_BE_NULL.add("DELETE"); + MUST_BE_NULL.add("GET"); + MUST_BE_NULL.add("HEAD"); + MUST_BE_NULL.add("TRACE"); + } @Override public void filter(ContainerRequestContext requestContext) throws IOException { @@ -44,6 +67,11 @@ public void filter(ContainerRequestContext requestContext) throws IOException { return; } + String redirectTag = requestContext.getHeaderString(X_HG_REDIRECT); + if (StringUtils.isNotEmpty(redirectTag)) { + return; + } + String url = ""; synchronized (instance) { if (instance.isMaster() || StringUtils.isEmpty(instance.url())) { @@ -54,7 +82,7 @@ public void filter(ContainerRequestContext requestContext) throws IOException { URI redirectUri = null; try { - URIBuilder redirectURIBuilder = new URIBuilder(requestContext.getUriInfo().getAbsolutePath()); + URIBuilder redirectURIBuilder = new URIBuilder(requestContext.getUriInfo().getRequestUri()); String[] host = url.split(":"); redirectURIBuilder.setHost(host[0]); if (host.length == 2 && StringUtils.isNotEmpty(host[1].trim())) { @@ -66,7 +94,47 @@ public void filter(ContainerRequestContext requestContext) throws IOException { LOG.error("Redirect request exception occurred", e); return; } - requestContext.abortWith(Response.temporaryRedirect(redirectUri).build()); + this.initClientIfNeeded(); + Response response = this.forwardRequest(requestContext, redirectUri); + requestContext.abortWith(response); + } + + private Response forwardRequest(ContainerRequestContext requestContext, URI redirectUri) { + MultivaluedMap headers = requestContext.getHeaders(); + MultivaluedMap newHeaders = HeaderUtils.createOutbound(); + if (headers != null) { + for (Map.Entry> entry : headers.entrySet()) { + for (String value : entry.getValue()) { + newHeaders.add(entry.getKey(), value); + } + } + } + newHeaders.add(X_HG_REDIRECT, new Date().getTime()); + Invocation.Builder builder = client.target(redirectUri) + .request() + .headers(newHeaders); + Response response = null; + if (MUST_BE_NULL.contains(requestContext.getMethod())) { + response = builder.method(requestContext.getMethod()); + } else { + response = builder.method(requestContext.getMethod(), + Entity.json(requestContext.getEntityStream())); + } + return response; + } + + private void initClientIfNeeded() { + if (client != null) { + return; + } + + synchronized (RedirectFilter.class) { + if (client != null) { + return; + } + + client = ClientBuilder.newClient(); + } } @NameBinding diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java index 62e7913678..aacfc2bb93 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java @@ -74,6 +74,7 @@ public class GremlinAPI extends API { @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=gremlin_execute"}) + @RedirectFilter.RedirectMasterRole public Map post(@Context GraphManager manager, @PathParam("graph") String graph, GremlinRequest request) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java index 0fe87ee0fd..7d00d63f09 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java @@ -53,6 +53,7 @@ public class RebuildAPI extends API { @Status(Status.ACCEPTED) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=index_write"}) + @RedirectFilter.RedirectMasterRole public Map vertexLabelRebuild(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { @@ -69,6 +70,7 @@ public Map vertexLabelRebuild(@Context GraphManager manager, @Status(Status.ACCEPTED) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=index_write"}) + @RedirectFilter.RedirectMasterRole public Map edgeLabelRebuild(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { @@ -85,6 +87,7 @@ public Map edgeLabelRebuild(@Context GraphManager manager, @Status(Status.ACCEPTED) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=index_write"}) + @RedirectFilter.RedirectMasterRole public Map indexLabelRebuild(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/raft/RaftAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/raft/RaftAPI.java index 44d5b6587b..dcf6cbb0d3 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/raft/RaftAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/raft/RaftAPI.java @@ -34,6 +34,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.slf4j.Logger; @@ -151,6 +152,7 @@ public Map setLeader(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin"}) + @RedirectFilter.RedirectMasterRole public Map addPeer(@Context GraphManager manager, @PathParam("graph") String graph, @QueryParam("group") @DefaultValue("default") @@ -180,6 +182,7 @@ public Map addPeer(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin"}) + @RedirectFilter.RedirectMasterRole public Map removePeer(@Context GraphManager manager, @PathParam("graph") String graph, @QueryParam("group") diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java index d72172c8dd..edca358807 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java @@ -41,9 +41,9 @@ public StandardStateMachineCallback(TaskManager taskManager) { public void onAsRoleMaster(StateMachineContext context) { if (!isMaster) { this.taskManager.onAsRoleMaster(); - this.initGlobalMasterInfo(context); LOG.info("Server {} change to master role", context.config().node()); } + this.initGlobalMasterInfo(context); this.isMaster = true; } @@ -51,10 +51,9 @@ public void onAsRoleMaster(StateMachineContext context) { public void onAsRoleWorker(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); - this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } - + this.initGlobalMasterInfo(context); this.isMaster = false; } @@ -66,9 +65,9 @@ public void onAsRoleCandidate(StateMachineContext context) { public void unknown(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); - this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } + this.initGlobalMasterInfo(context); isMaster = false; } @@ -77,9 +76,9 @@ public void unknown(StateMachineContext context) { public void onAsRoleAbdication(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); - this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } + this.initGlobalMasterInfo(context); isMaster = false; }