diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java index 71d42e3452..cf07bdfbe7 100644 --- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java +++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java @@ -58,7 +58,7 @@ public static T mapToObject(Map map, Class beanClass) { return null; } Object obj = OBJECT_MAPPER.convertValue(map, beanClass); - return (T) obj; + return beanClass.cast(obj); } /** diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java index 0b0a6afedf..cdfe4e163a 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java @@ -35,7 +35,9 @@ import com.alibaba.fastjson2.JSONWriter; import lombok.Data; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Data public abstract class AbstractHttpHandler implements HttpHandler { @@ -83,6 +85,12 @@ protected void writeUnauthorized(ChannelHandlerContext ctx, String message) { writeJson(ctx, json, HttpResponseStatus.UNAUTHORIZED); } + protected void writeInternalServerError(ChannelHandlerContext ctx, String message) { + Result result = new Result<>(message); + String json = JSON.toJSONString(result, JSONWriter.Feature.WriteNulls); + writeJson(ctx, json, HttpResponseStatus.INTERNAL_SERVER_ERROR); + } + /** * Use {@link HttpResponseUtils#buildHttpResponse} to build {@link HttpResponse} param. */ @@ -92,25 +100,36 @@ protected void write(ChannelHandlerContext ctx, HttpResponse response) { @Override public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { - switch (HttpMethod.valueOf(httpRequest.method().name())) { - case OPTIONS: - preflight(ctx); - break; - case GET: - get(httpRequest, ctx); - break; - case POST: - post(httpRequest, ctx); - break; - case PUT: - put(httpRequest, ctx); - break; - case DELETE: - delete(httpRequest, ctx); - break; - default: - // do nothing - break; + try { + switch (HttpMethod.valueOf(httpRequest.method().name())) { + case OPTIONS: + preflight(ctx); + break; + case GET: + get(httpRequest, ctx); + break; + case POST: + post(httpRequest, ctx); + break; + case PUT: + put(httpRequest, ctx); + break; + case DELETE: + delete(httpRequest, ctx); + break; + default: // do nothing + } + } catch (RuntimeException e) { + StackTraceElement element = e.getStackTrace()[0]; + String className = element.getClassName(); + String handlerName = className.substring(className.lastIndexOf(".") + 1); + if (e instanceof IllegalArgumentException) { + log.warn("Admin endpoint {}:{} - {}", handlerName, element.getLineNumber(), e.getMessage()); + writeBadRequest(ctx, e.getMessage()); + } else { + log.error("Admin endpoint {}:{} - {}", handlerName, element.getLineNumber(), e.getMessage(), e); + writeInternalServerError(ctx, e.getMessage()); + } } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java index 4f81584b9d..1da928dc10 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java @@ -58,6 +58,8 @@ public class AdminHandlerManager { + private EventMeshServer eventMeshServer; + private EventMeshTCPServer eventMeshTCPServer; private EventMeshHTTPServer eventMeshHTTPServer; @@ -71,9 +73,10 @@ public class AdminHandlerManager { private final Map httpHandlerMap = new ConcurrentHashMap<>(); public AdminHandlerManager(EventMeshServer eventMeshServer) { + this.eventMeshServer = eventMeshServer; + this.eventMeshTCPServer = eventMeshServer.getEventMeshTCPServer(); this.eventMeshGrpcServer = eventMeshServer.getEventMeshGrpcServer(); this.eventMeshHTTPServer = eventMeshServer.getEventMeshHTTPServer(); - this.eventMeshTCPServer = eventMeshServer.getEventMeshTCPServer(); this.eventMeshMetaStorage = eventMeshServer.getMetaStorage(); this.adminWebHookConfigOperationManage = eventMeshTCPServer.getAdminWebHookConfigOperationManage(); } @@ -112,6 +115,7 @@ public void registerHttpHandler() { // v2 endpoints initHandler(new ConfigurationHandler( + eventMeshServer.getConfiguration(), eventMeshTCPServer.getEventMeshTCPConfiguration(), eventMeshHTTPServer.getEventMeshHttpConfiguration(), eventMeshGrpcServer.getEventMeshGrpcConfiguration())); diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/v2/ConfigurationHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/v2/ConfigurationHandler.java index ce16b95749..1c149ca521 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/v2/ConfigurationHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/v2/ConfigurationHandler.java @@ -39,6 +39,7 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.filter.Filter; import com.alibaba.fastjson2.filter.NameFilter; +import com.alibaba.fastjson2.filter.PropertyFilter; import com.alibaba.fastjson2.filter.ValueFilter; import lombok.extern.slf4j.Slf4j; @@ -56,6 +57,7 @@ @EventMeshHttpHandler(path = "/v2/configuration") public class ConfigurationHandler extends AbstractHttpHandler { + private final CommonConfiguration commonConfiguration; private final EventMeshTCPConfiguration eventMeshTCPConfiguration; private final EventMeshHTTPConfiguration eventMeshHTTPConfiguration; private final EventMeshGrpcConfiguration eventMeshGrpcConfiguration; @@ -68,10 +70,12 @@ public class ConfigurationHandler extends AbstractHttpHandler { * @param eventMeshGrpcConfiguration the gRPC configuration for EventMesh */ public ConfigurationHandler( + CommonConfiguration commonConfiguration, EventMeshTCPConfiguration eventMeshTCPConfiguration, EventMeshHTTPConfiguration eventMeshHTTPConfiguration, EventMeshGrpcConfiguration eventMeshGrpcConfiguration) { super(); + this.commonConfiguration = commonConfiguration; this.eventMeshTCPConfiguration = eventMeshTCPConfiguration; this.eventMeshHTTPConfiguration = eventMeshHTTPConfiguration; this.eventMeshGrpcConfiguration = eventMeshGrpcConfiguration; @@ -85,34 +89,51 @@ public ConfigurationHandler( *

When {@code properties}, the field names are returned in Properties format; *

When {@code bean}, the field names themselves are used as json keys. * + *

  • + * {@code configs}: String; Optional, DefaultValue: {@code exclusive}, SelectableValue: {@code all}. + *

    When {@code exclusive}, protocol-specific configurations will only contain protocol-exclusive fields + * and won't contain any {@link CommonConfiguration} fields; + *

    When {@code all}, protocol-specific configurations will contain all fields, including those in {@link CommonConfiguration}. + *

  • * */ @Override protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) { String format = HttpRequestUtil.getQueryParam(httpRequest, "format", "properties"); - - Filter[] filters; - if (format.equals("properties")) { - filters = new Filter[] {new ConfigFieldFilter(), new IPAddressToStringFilter()}; - } else if (format.equals("bean")) { - filters = new Filter[] {new IPAddressToStringFilter()}; - } else { - log.warn("Invalid format param: {}", format); - writeBadRequest(ctx, "Invalid format param: " + format); - return; + String configs = HttpRequestUtil.getQueryParam(httpRequest, "configs", "exclusive"); + + List filters = new ArrayList<>(); + switch (configs) { + case "exclusive": + filters.add(new SuperClassFieldFilter()); + break; + case "all": break; + default: + throw new IllegalArgumentException("Invalid param 'configs': " + configs); + } + switch (format) { + case "properties": + filters.add(new ConfigFieldFilter()); + break; + case "bean": break; + default: + throw new IllegalArgumentException("Invalid param 'format': " + format); } + filters.add(new IPAddressToStringFilter()); GetConfigurationResponse getConfigurationResponse = new GetConfigurationResponse( + commonConfiguration, eventMeshTCPConfiguration, eventMeshHTTPConfiguration, - eventMeshGrpcConfiguration + eventMeshGrpcConfiguration, + "v1.10.0-release" // TODO get version number after merging https://github.com/apache/eventmesh/pull/4055 ); - String json = JSON.toJSONString(Result.success(getConfigurationResponse), filters); + String json = JSON.toJSONString(Result.success(getConfigurationResponse), filters.toArray(new Filter[0])); writeJson(ctx, json); } /** - * For each member of {@link EventMeshTCPConfiguration}, {@link EventMeshHTTPConfiguration}, and {@link EventMeshGrpcConfiguration}, + * For each member of configuration classes, * the value of the {@link ConfigField} annotation for each field is obtained through reflection, * and then concatenated with the configuration prefix in the {@link Config} annotation to serve as the JSON key for this field. *

    @@ -155,6 +176,39 @@ private Field findFieldInClassHierarchy(Class clazz, String fieldName) throws } } + /** + * For each member of {@link EventMeshTCPConfiguration}, {@link EventMeshHTTPConfiguration}, and {@link EventMeshGrpcConfiguration}, + * if the {@code name} is a member that exists in {@link CommonConfiguration} class, it will be skipped. + */ + static class SuperClassFieldFilter implements PropertyFilter { + @Override + public boolean apply(Object object, String name, Object value) { + try { + Field field = findFieldInClassNonHierarchy(object.getClass(), name); + return field != null; + } catch (NoSuchFieldException e) { + log.error("Failed to get field {} from object {}", name, object, e); + } + return true; + } + + /** + * If a field of a subclass exists in the superclass, return null, causing FastJSON to skip this field. + */ + private Field findFieldInClassNonHierarchy(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superclass = clazz.getSuperclass(); + if (superclass == null) { + throw e; + } else { + return null; + } + } + } + } + /** * {@link IPAddress} can't be serialized directly by FastJSON, * so this filter converts {@link IPAddress} objects to their string representation. diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/response/v2/GetConfigurationResponse.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/response/v2/GetConfigurationResponse.java index 40161cca73..5ae9eeb2d3 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/response/v2/GetConfigurationResponse.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/response/v2/GetConfigurationResponse.java @@ -17,6 +17,7 @@ package org.apache.eventmesh.runtime.admin.response.v2; +import org.apache.eventmesh.common.config.CommonConfiguration; import org.apache.eventmesh.runtime.configuration.EventMeshGrpcConfiguration; import org.apache.eventmesh.runtime.configuration.EventMeshHTTPConfiguration; import org.apache.eventmesh.runtime.configuration.EventMeshTCPConfiguration; @@ -30,7 +31,9 @@ @AllArgsConstructor public class GetConfigurationResponse { + private CommonConfiguration commonConfiguration; private EventMeshTCPConfiguration eventMeshTCPConfiguration; private EventMeshHTTPConfiguration eventMeshHTTPConfiguration; private EventMeshGrpcConfiguration eventMeshGrpcConfiguration; + private String eventMeshVersion; }