Skip to content

Commit 70f9892

Browse files
authored
[ISSUE #4834] Add protocol exclusive fields filter for /v2/configuration endpoint (#4835)
* Extract commonConfiguration out of subclasses * Add configs param * Unify return position * Catch more exceptions
1 parent 86e113f commit 70f9892

File tree

5 files changed

+114
-34
lines changed

5 files changed

+114
-34
lines changed

Diff for: eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) {
5858
return null;
5959
}
6060
Object obj = OBJECT_MAPPER.convertValue(map, beanClass);
61-
return (T) obj;
61+
return beanClass.cast(obj);
6262
}
6363

6464
/**

Diff for: eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java

+38-19
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import com.alibaba.fastjson2.JSONWriter;
3636

3737
import lombok.Data;
38+
import lombok.extern.slf4j.Slf4j;
3839

40+
@Slf4j
3941
@Data
4042
public abstract class AbstractHttpHandler implements HttpHandler {
4143

@@ -83,6 +85,12 @@ protected void writeUnauthorized(ChannelHandlerContext ctx, String message) {
8385
writeJson(ctx, json, HttpResponseStatus.UNAUTHORIZED);
8486
}
8587

88+
protected void writeInternalServerError(ChannelHandlerContext ctx, String message) {
89+
Result<String> result = new Result<>(message);
90+
String json = JSON.toJSONString(result, JSONWriter.Feature.WriteNulls);
91+
writeJson(ctx, json, HttpResponseStatus.INTERNAL_SERVER_ERROR);
92+
}
93+
8694
/**
8795
* Use {@link HttpResponseUtils#buildHttpResponse} to build {@link HttpResponse} param.
8896
*/
@@ -92,25 +100,36 @@ protected void write(ChannelHandlerContext ctx, HttpResponse response) {
92100

93101
@Override
94102
public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception {
95-
switch (HttpMethod.valueOf(httpRequest.method().name())) {
96-
case OPTIONS:
97-
preflight(ctx);
98-
break;
99-
case GET:
100-
get(httpRequest, ctx);
101-
break;
102-
case POST:
103-
post(httpRequest, ctx);
104-
break;
105-
case PUT:
106-
put(httpRequest, ctx);
107-
break;
108-
case DELETE:
109-
delete(httpRequest, ctx);
110-
break;
111-
default:
112-
// do nothing
113-
break;
103+
try {
104+
switch (HttpMethod.valueOf(httpRequest.method().name())) {
105+
case OPTIONS:
106+
preflight(ctx);
107+
break;
108+
case GET:
109+
get(httpRequest, ctx);
110+
break;
111+
case POST:
112+
post(httpRequest, ctx);
113+
break;
114+
case PUT:
115+
put(httpRequest, ctx);
116+
break;
117+
case DELETE:
118+
delete(httpRequest, ctx);
119+
break;
120+
default: // do nothing
121+
}
122+
} catch (RuntimeException e) {
123+
StackTraceElement element = e.getStackTrace()[0];
124+
String className = element.getClassName();
125+
String handlerName = className.substring(className.lastIndexOf(".") + 1);
126+
if (e instanceof IllegalArgumentException) {
127+
log.warn("Admin endpoint {}:{} - {}", handlerName, element.getLineNumber(), e.getMessage());
128+
writeBadRequest(ctx, e.getMessage());
129+
} else {
130+
log.error("Admin endpoint {}:{} - {}", handlerName, element.getLineNumber(), e.getMessage(), e);
131+
writeInternalServerError(ctx, e.getMessage());
132+
}
114133
}
115134
}
116135

Diff for: eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858

5959
public class AdminHandlerManager {
6060

61+
private EventMeshServer eventMeshServer;
62+
6163
private EventMeshTCPServer eventMeshTCPServer;
6264

6365
private EventMeshHTTPServer eventMeshHTTPServer;
@@ -71,9 +73,10 @@ public class AdminHandlerManager {
7173
private final Map<String, HttpHandler> httpHandlerMap = new ConcurrentHashMap<>();
7274

7375
public AdminHandlerManager(EventMeshServer eventMeshServer) {
76+
this.eventMeshServer = eventMeshServer;
77+
this.eventMeshTCPServer = eventMeshServer.getEventMeshTCPServer();
7478
this.eventMeshGrpcServer = eventMeshServer.getEventMeshGrpcServer();
7579
this.eventMeshHTTPServer = eventMeshServer.getEventMeshHTTPServer();
76-
this.eventMeshTCPServer = eventMeshServer.getEventMeshTCPServer();
7780
this.eventMeshMetaStorage = eventMeshServer.getMetaStorage();
7881
this.adminWebHookConfigOperationManage = eventMeshTCPServer.getAdminWebHookConfigOperationManage();
7982
}
@@ -112,6 +115,7 @@ public void registerHttpHandler() {
112115

113116
// v2 endpoints
114117
initHandler(new ConfigurationHandler(
118+
eventMeshServer.getConfiguration(),
115119
eventMeshTCPServer.getEventMeshTCPConfiguration(),
116120
eventMeshHTTPServer.getEventMeshHttpConfiguration(),
117121
eventMeshGrpcServer.getEventMeshGrpcConfiguration()));

Diff for: eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/v2/ConfigurationHandler.java

+67-13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.alibaba.fastjson2.JSON;
4040
import com.alibaba.fastjson2.filter.Filter;
4141
import com.alibaba.fastjson2.filter.NameFilter;
42+
import com.alibaba.fastjson2.filter.PropertyFilter;
4243
import com.alibaba.fastjson2.filter.ValueFilter;
4344

4445
import lombok.extern.slf4j.Slf4j;
@@ -56,6 +57,7 @@
5657
@EventMeshHttpHandler(path = "/v2/configuration")
5758
public class ConfigurationHandler extends AbstractHttpHandler {
5859

60+
private final CommonConfiguration commonConfiguration;
5961
private final EventMeshTCPConfiguration eventMeshTCPConfiguration;
6062
private final EventMeshHTTPConfiguration eventMeshHTTPConfiguration;
6163
private final EventMeshGrpcConfiguration eventMeshGrpcConfiguration;
@@ -68,10 +70,12 @@ public class ConfigurationHandler extends AbstractHttpHandler {
6870
* @param eventMeshGrpcConfiguration the gRPC configuration for EventMesh
6971
*/
7072
public ConfigurationHandler(
73+
CommonConfiguration commonConfiguration,
7174
EventMeshTCPConfiguration eventMeshTCPConfiguration,
7275
EventMeshHTTPConfiguration eventMeshHTTPConfiguration,
7376
EventMeshGrpcConfiguration eventMeshGrpcConfiguration) {
7477
super();
78+
this.commonConfiguration = commonConfiguration;
7579
this.eventMeshTCPConfiguration = eventMeshTCPConfiguration;
7680
this.eventMeshHTTPConfiguration = eventMeshHTTPConfiguration;
7781
this.eventMeshGrpcConfiguration = eventMeshGrpcConfiguration;
@@ -85,34 +89,51 @@ public ConfigurationHandler(
8589
* <p>When {@code properties}, the field names are returned in Properties format;
8690
* <p>When {@code bean}, the field names themselves are used as json keys.
8791
* </li>
92+
* <li>
93+
* {@code configs}: String; Optional, DefaultValue: {@code exclusive}, SelectableValue: {@code all}.
94+
* <p>When {@code exclusive}, protocol-specific configurations will only contain protocol-exclusive fields
95+
* and won't contain any {@link CommonConfiguration} fields;
96+
* <p>When {@code all}, protocol-specific configurations will contain all fields, including those in {@link CommonConfiguration}.
97+
* </li>
8898
* </ul>
8999
*/
90100
@Override
91101
protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) {
92102
String format = HttpRequestUtil.getQueryParam(httpRequest, "format", "properties");
93-
94-
Filter[] filters;
95-
if (format.equals("properties")) {
96-
filters = new Filter[] {new ConfigFieldFilter(), new IPAddressToStringFilter()};
97-
} else if (format.equals("bean")) {
98-
filters = new Filter[] {new IPAddressToStringFilter()};
99-
} else {
100-
log.warn("Invalid format param: {}", format);
101-
writeBadRequest(ctx, "Invalid format param: " + format);
102-
return;
103+
String configs = HttpRequestUtil.getQueryParam(httpRequest, "configs", "exclusive");
104+
105+
List<Filter> filters = new ArrayList<>();
106+
switch (configs) {
107+
case "exclusive":
108+
filters.add(new SuperClassFieldFilter());
109+
break;
110+
case "all": break;
111+
default:
112+
throw new IllegalArgumentException("Invalid param 'configs': " + configs);
113+
}
114+
switch (format) {
115+
case "properties":
116+
filters.add(new ConfigFieldFilter());
117+
break;
118+
case "bean": break;
119+
default:
120+
throw new IllegalArgumentException("Invalid param 'format': " + format);
103121
}
122+
filters.add(new IPAddressToStringFilter());
104123

105124
GetConfigurationResponse getConfigurationResponse = new GetConfigurationResponse(
125+
commonConfiguration,
106126
eventMeshTCPConfiguration,
107127
eventMeshHTTPConfiguration,
108-
eventMeshGrpcConfiguration
128+
eventMeshGrpcConfiguration,
129+
"v1.10.0-release" // TODO get version number after merging https://github.com/apache/eventmesh/pull/4055
109130
);
110-
String json = JSON.toJSONString(Result.success(getConfigurationResponse), filters);
131+
String json = JSON.toJSONString(Result.success(getConfigurationResponse), filters.toArray(new Filter[0]));
111132
writeJson(ctx, json);
112133
}
113134

114135
/**
115-
* For each member of {@link EventMeshTCPConfiguration}, {@link EventMeshHTTPConfiguration}, and {@link EventMeshGrpcConfiguration},
136+
* For each member of configuration classes,
116137
* the value of the {@link ConfigField} annotation for each field is obtained through reflection,
117138
* and then concatenated with the configuration prefix in the {@link Config} annotation to serve as the JSON key for this field.
118139
* <p>
@@ -155,6 +176,39 @@ private Field findFieldInClassHierarchy(Class<?> clazz, String fieldName) throws
155176
}
156177
}
157178

179+
/**
180+
* For each member of {@link EventMeshTCPConfiguration}, {@link EventMeshHTTPConfiguration}, and {@link EventMeshGrpcConfiguration},
181+
* if the {@code name} is a member that exists in {@link CommonConfiguration} class, it will be skipped.
182+
*/
183+
static class SuperClassFieldFilter implements PropertyFilter {
184+
@Override
185+
public boolean apply(Object object, String name, Object value) {
186+
try {
187+
Field field = findFieldInClassNonHierarchy(object.getClass(), name);
188+
return field != null;
189+
} catch (NoSuchFieldException e) {
190+
log.error("Failed to get field {} from object {}", name, object, e);
191+
}
192+
return true;
193+
}
194+
195+
/**
196+
* If a field of a subclass exists in the superclass, return null, causing FastJSON to skip this field.
197+
*/
198+
private Field findFieldInClassNonHierarchy(Class<?> clazz, String fieldName) throws NoSuchFieldException {
199+
try {
200+
return clazz.getDeclaredField(fieldName);
201+
} catch (NoSuchFieldException e) {
202+
Class<?> superclass = clazz.getSuperclass();
203+
if (superclass == null) {
204+
throw e;
205+
} else {
206+
return null;
207+
}
208+
}
209+
}
210+
}
211+
158212
/**
159213
* {@link IPAddress} can't be serialized directly by FastJSON,
160214
* so this filter converts {@link IPAddress} objects to their string representation.

Diff for: eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/response/v2/GetConfigurationResponse.java

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.eventmesh.runtime.admin.response.v2;
1919

20+
import org.apache.eventmesh.common.config.CommonConfiguration;
2021
import org.apache.eventmesh.runtime.configuration.EventMeshGrpcConfiguration;
2122
import org.apache.eventmesh.runtime.configuration.EventMeshHTTPConfiguration;
2223
import org.apache.eventmesh.runtime.configuration.EventMeshTCPConfiguration;
@@ -30,7 +31,9 @@
3031
@AllArgsConstructor
3132
public class GetConfigurationResponse {
3233

34+
private CommonConfiguration commonConfiguration;
3335
private EventMeshTCPConfiguration eventMeshTCPConfiguration;
3436
private EventMeshHTTPConfiguration eventMeshHTTPConfiguration;
3537
private EventMeshGrpcConfiguration eventMeshGrpcConfiguration;
38+
private String eventMeshVersion;
3639
}

0 commit comments

Comments
 (0)