Skip to content

Commit cb904ab

Browse files
authored
[REST Compatible API] Route refactoring (#69573) (#70109)
Makes `Route`s `RestApiVersion` -aware. Refactors how `Route`s are constructed in the case of deprecation or replacement of routes.
1 parent 791d35f commit cb904ab

File tree

158 files changed

+1056
-1515
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

158 files changed

+1056
-1515
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.common;
10+
11+
/**
12+
* Vestigial marker to make v8-style version-aware routing backportable to 7.x.
13+
*/
14+
public enum RestApiVersion {
15+
16+
V_7;
17+
18+
public static RestApiVersion current() {
19+
return V_7;
20+
}
21+
22+
}

server/src/main/java/org/elasticsearch/rest/BaseRestHandler.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,6 @@ public List<Route> routes() {
206206
return delegate.routes();
207207
}
208208

209-
@Override
210-
public List<DeprecatedRoute> deprecatedRoutes() {
211-
return delegate.deprecatedRoutes();
212-
}
213-
214-
@Override
215-
public List<ReplacedRoute> replacedRoutes() {
216-
return delegate.replacedRoutes();
217-
}
218-
219209
@Override
220210
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
221211
return delegate.prepareRequest(request, client);

server/src/main/java/org/elasticsearch/rest/MethodHandlers.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,23 @@ final class MethodHandlers {
2222
private final String path;
2323
private final Map<RestRequest.Method, RestHandler> methodHandlers;
2424

25-
MethodHandlers(String path, RestHandler handler, RestRequest.Method... methods) {
25+
MethodHandlers(String path) {
2626
this.path = path;
27-
this.methodHandlers = new HashMap<>(methods.length);
28-
for (RestRequest.Method method : methods) {
29-
methodHandlers.put(method, handler);
30-
}
27+
// by setting the loadFactor to 1, these maps are resized only when they *must* be, and the vast majority of these
28+
// maps contain only 1 or 2 entries anyway, so most of these maps are never resized at all and waste only 1 or 0
29+
// array references, while those few that contain 3 or 4 elements will have been resized just once and will still
30+
// waste only 1 or 0 array references
31+
this.methodHandlers = new HashMap<>(2, 1);
3132
}
3233

3334
/**
3435
* Add a handler for an additional array of methods. Note that {@code MethodHandlers}
3536
* does not allow replacing the handler for an already existing method.
3637
*/
37-
MethodHandlers addMethods(RestHandler handler, RestRequest.Method... methods) {
38-
for (RestRequest.Method method : methods) {
39-
RestHandler existing = methodHandlers.putIfAbsent(method, handler);
40-
if (existing != null) {
41-
throw new IllegalArgumentException("Cannot replace existing handler for [" + path + "] for method: " + method);
42-
}
38+
MethodHandlers addMethod(RestRequest.Method method, RestHandler handler) {
39+
RestHandler existing = methodHandlers.putIfAbsent(method, handler);
40+
if (existing != null) {
41+
throw new IllegalArgumentException("Cannot replace existing handler for [" + path + "] for method: " + method);
4342
}
4443
return this;
4544
}

server/src/main/java/org/elasticsearch/rest/RestController.java

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.core.internal.io.Streams;
2828
import org.elasticsearch.http.HttpServerTransport;
2929
import org.elasticsearch.indices.breaker.CircuitBreakerService;
30+
import org.elasticsearch.rest.RestHandler.Route;
3031
import org.elasticsearch.usage.UsageService;
3132

3233
import java.io.ByteArrayOutputStream;
@@ -92,64 +93,65 @@ public RestController(Set<RestHeaderDefinition> headersToCopy, UnaryOperator<Res
9293
this.handlerWrapper = handlerWrapper;
9394
this.client = client;
9495
this.circuitBreakerService = circuitBreakerService;
95-
registerHandlerNoWrap(RestRequest.Method.GET, "/favicon.ico", (request, channel, clnt) ->
96+
registerHandlerNoWrap(RestRequest.Method.GET, "/favicon.ico",
97+
(request, channel, clnt) ->
9698
channel.sendResponse(new BytesRestResponse(RestStatus.OK, "image/x-icon", FAVICON_RESPONSE)));
9799
}
98100

99101
/**
100102
* Registers a REST handler to be executed when the provided {@code method} and {@code path} match the request.
101103
*
102104
* @param method GET, POST, etc.
103-
* @param path Path to handle (e.g., "/{index}/{type}/_bulk")
105+
* @param path Path to handle (e.g. "/{index}/{type}/_bulk")
104106
* @param handler The handler to actually execute
105107
* @param deprecationMessage The message to log and send as a header in the response
106108
*/
107-
protected void registerAsDeprecatedHandler(RestRequest.Method method, String path, RestHandler handler, String deprecationMessage) {
109+
protected void registerAsDeprecatedHandler(RestRequest.Method method, String path,
110+
RestHandler handler, String deprecationMessage) {
108111
assert (handler instanceof DeprecationRestHandler) == false;
109-
110112
registerHandler(method, path, new DeprecationRestHandler(handler, deprecationMessage, deprecationLogger));
111113
}
112114

113115
/**
114116
* Registers a REST handler to be executed when the provided {@code method} and {@code path} match the request, or when provided
115-
* with {@code deprecatedMethod} and {@code deprecatedPath}. Expected usage:
117+
* with {@code replacedMethod} and {@code replacedPath}. Expected usage:
116118
* <pre><code>
117119
* // remove deprecation in next major release
118-
* controller.registerWithDeprecatedHandler(POST, "/_forcemerge", this,
119-
* POST, "/_optimize", deprecationLogger);
120-
* controller.registerWithDeprecatedHandler(POST, "/{index}/_forcemerge", this,
121-
* POST, "/{index}/_optimize", deprecationLogger);
120+
* controller.registerAsDeprecatedHandler(POST, "/_forcemerge", RestApiVersion.V_8, someHandler,
121+
* POST, "/_optimize", RestApiVersion.V_7);
122+
* controller.registerAsDeprecatedHandler(POST, "/{index}/_forcemerge", RestApiVersion.V_8, someHandler,
123+
* POST, "/{index}/_optimize", RestApiVersion.V_7);
122124
* </code></pre>
123125
* <p>
124126
* The registered REST handler ({@code method} with {@code path}) is a normal REST handler that is not deprecated and it is
125-
* replacing the deprecated REST handler ({@code deprecatedMethod} with {@code deprecatedPath}) that is using the <em>same</em>
127+
* replacing the deprecated REST handler ({@code replacedMethod} with {@code replacedPath}) that is using the <em>same</em>
126128
* {@code handler}.
127129
* <p>
128130
* Deprecated REST handlers without a direct replacement should be deprecated directly using {@link #registerAsDeprecatedHandler}
129131
* and a specific message.
130132
*
131133
* @param method GET, POST, etc.
132-
* @param path Path to handle (e.g., "/_forcemerge")
134+
* @param path Path to handle (e.g. "/_forcemerge")
133135
* @param handler The handler to actually execute
134-
* @param deprecatedMethod GET, POST, etc.
135-
* @param deprecatedPath <em>Deprecated</em> path to handle (e.g., "/_optimize")
136+
* @param replacedMethod GET, POST, etc.
137+
* @param replacedPath <em>Replaced</em> path to handle (e.g. "/_optimize")
136138
*/
137-
protected void registerWithDeprecatedHandler(RestRequest.Method method, String path, RestHandler handler,
138-
RestRequest.Method deprecatedMethod, String deprecatedPath) {
139-
// e.g., [POST /_optimize] is deprecated! Use [POST /_forcemerge] instead.
140-
final String deprecationMessage =
141-
"[" + deprecatedMethod.name() + " " + deprecatedPath + "] is deprecated! Use [" + method.name() + " " + path + "] instead.";
139+
protected void registerAsReplacedHandler(RestRequest.Method method, String path, RestHandler handler,
140+
RestRequest.Method replacedMethod, String replacedPath) {
141+
// e.g. [POST /_optimize] is deprecated! Use [POST /_forcemerge] instead.
142+
final String replacedMessage =
143+
"[" + replacedMethod.name() + " " + replacedPath + "] is deprecated! Use [" + method.name() + " " + path + "] instead.";
142144

143145
registerHandler(method, path, handler);
144-
registerAsDeprecatedHandler(deprecatedMethod, deprecatedPath, handler, deprecationMessage);
146+
registerAsDeprecatedHandler(replacedMethod, replacedPath, handler, replacedMessage);
145147
}
146148

147149
/**
148150
* Registers a REST handler to be executed when one of the provided methods and path match the request.
149151
*
150-
* @param path Path to handle (e.g., "/{index}/{type}/_bulk")
151-
* @param handler The handler to actually execute
152152
* @param method GET, POST, etc.
153+
* @param path Path to handle (e.g. "/{index}/{type}/_bulk")
154+
* @param handler The handler to actually execute
153155
*/
154156
protected void registerHandler(RestRequest.Method method, String path, RestHandler handler) {
155157
if (handler instanceof BaseRestHandler) {
@@ -159,20 +161,29 @@ protected void registerHandler(RestRequest.Method method, String path, RestHandl
159161
}
160162

161163
private void registerHandlerNoWrap(RestRequest.Method method, String path, RestHandler maybeWrappedHandler) {
162-
handlers.insertOrUpdate(path, new MethodHandlers(path, maybeWrappedHandler, method),
163-
(mHandlers, newMHandler) -> mHandlers.addMethods(maybeWrappedHandler, method));
164+
handlers.insertOrUpdate(path,
165+
new MethodHandlers(path).addMethod(method, maybeWrappedHandler),
166+
(mHandlers, newMHandler) -> mHandlers.addMethod(method, maybeWrappedHandler));
167+
}
168+
169+
public void registerHandler(final Route route, final RestHandler handler) {
170+
if (route.isReplacement()) {
171+
Route replaced = route.getReplacedRoute();
172+
registerAsReplacedHandler(route.getMethod(), route.getPath(), handler, replaced.getMethod(), replaced.getPath());
173+
} else if (route.isDeprecated()) {
174+
registerAsDeprecatedHandler(route.getMethod(), route.getPath(), handler, route.getDeprecationMessage());
175+
} else {
176+
// it's just a normal route
177+
registerHandler(route.getMethod(), route.getPath(), handler);
178+
}
164179
}
165180

166181
/**
167182
* Registers a REST handler with the controller. The REST handler declares the {@code method}
168183
* and {@code path} combinations.
169184
*/
170-
public void registerHandler(final RestHandler restHandler) {
171-
restHandler.routes().forEach(route -> registerHandler(route.getMethod(), route.getPath(), restHandler));
172-
restHandler.deprecatedRoutes().forEach(route ->
173-
registerAsDeprecatedHandler(route.getMethod(), route.getPath(), restHandler, route.getDeprecationMessage()));
174-
restHandler.replacedRoutes().forEach(route -> registerWithDeprecatedHandler(route.getMethod(), route.getPath(),
175-
restHandler, route.getDeprecatedMethod(), route.getDeprecatedPath()));
185+
public void registerHandler(final RestHandler handler) {
186+
handler.routes().forEach(route -> registerHandler(route, handler));
176187
}
177188

178189
@Override
@@ -312,7 +323,7 @@ private void tryAllHandlers(final RestRequest request, final RestChannel channel
312323
// we consume the error_trace parameter first to ensure that it is always consumed
313324
if (request.paramAsBoolean("error_trace", false) && channel.detailedErrorsEnabled() == false) {
314325
channel.sendResponse(
315-
BytesRestResponse.createSimpleErrorResponse(channel, BAD_REQUEST, "error traces in responses are disabled."));
326+
BytesRestResponse.createSimpleErrorResponse(channel, BAD_REQUEST, "error traces in responses are disabled."));
316327
return;
317328
}
318329

@@ -333,9 +344,9 @@ private void tryAllHandlers(final RestRequest request, final RestChannel channel
333344
handler = handlers.getHandler(requestMethod);
334345
}
335346
if (handler == null) {
336-
if (handleNoHandlerFound(rawPath, requestMethod, uri, channel)) {
337-
return;
338-
}
347+
if (handleNoHandlerFound(rawPath, requestMethod, uri, channel)) {
348+
return;
349+
}
339350
} else {
340351
dispatchRequest(request, channel, handler);
341352
return;

0 commit comments

Comments
 (0)