From 66a44a9af06fc140af2ce74f2f4538e9bcb78f1f Mon Sep 17 00:00:00 2001 From: Montos <1367654518@qq.com> Date: Thu, 27 Apr 2023 18:45:20 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update pom.xml 修复tkmybatis序列化循环引用 修复tkmybatis序列化循环引用 --- .../java/io/arex/foundation/serializer/JacksonSerializer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java b/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java index 2fd90d476..2be504b8f 100644 --- a/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java +++ b/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.time.FastDateFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tk.mybatis.mapper.entity.EntityTable; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; @@ -168,6 +169,8 @@ private void configMapper() { MAPPER.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); MAPPER.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); + + MAPPER.addMixIn(EntityTable.class,IgnoreType.class); } private void customTimeFormatSerializer(SimpleModule module) { From aa2d5229814067a4192219662dc1475232112466 Mon Sep 17 00:00:00 2001 From: MentosL <1367654518@qq.com> Date: Wed, 3 May 2023 23:46:22 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=88=9D=E6=AC=A1mqtt?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加mqtt相关参数 --- .../bootstrap/model/MockCategoryType.java | 2 + arex-agent/pom.xml | 5 ++ .../io/arex/inst/runtime/util/MockUtils.java | 5 ++ .../mq/arex-integration-mqtt/pom.xml | 23 +++++++ .../io/arex/inst/mqtt/MQTTAdapterHelper.java | 16 +++++ .../mqtt/inst/EclipseInstrumentationV3.java | 63 +++++++++++++++++++ .../MQTTAdapterModuleInstrumentation.java | 24 +++++++ arex-instrumentation/pom.xml | 1 + 8 files changed, 139 insertions(+) create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/pom.xml create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java diff --git a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/model/MockCategoryType.java b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/model/MockCategoryType.java index eb29e60bb..d6ee98e5e 100644 --- a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/model/MockCategoryType.java +++ b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/model/MockCategoryType.java @@ -19,6 +19,8 @@ public class MockCategoryType implements Serializable { public static final MockCategoryType DUBBO_CONSUMER = createDependency("DubboConsumer"); public static final MockCategoryType DUBBO_PROVIDER = createEntryPoint("DubboProvider"); public static final MockCategoryType DUBBO_STREAM_PROVIDER = createDependency("DubboStreamProvider"); + public static final MockCategoryType MQTT_MESSAGE_CONSUMER = createEntryPoint("MqttMessageConsumer"); + private String name; private boolean entryPoint; diff --git a/arex-agent/pom.xml b/arex-agent/pom.xml index 968510c40..02f314815 100644 --- a/arex-agent/pom.xml +++ b/arex-agent/pom.xml @@ -147,6 +147,11 @@ arex-jcasbin ${project.version} + + ${project.groupId} + arex-integration-mqtt + ${project.version} + diff --git a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java index b29da4d89..3f398249b 100644 --- a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java +++ b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java @@ -68,6 +68,11 @@ public static ArexMocker createDubboStreamProvider(String operationName) { return create(MockCategoryType.DUBBO_STREAM_PROVIDER, operationName); } + public static ArexMocker createMqttConsumer(String operationName){ + return create(MockCategoryType.MQTT_MESSAGE_CONSUMER,operationName); + } + + public static ArexMocker create(MockCategoryType categoryType, String operationName) { ArexMocker mocker = new ArexMocker(); long createTime = System.currentTimeMillis(); diff --git a/arex-instrumentation/mq/arex-integration-mqtt/pom.xml b/arex-instrumentation/mq/arex-integration-mqtt/pom.xml new file mode 100644 index 000000000..6e84a2f46 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/pom.xml @@ -0,0 +1,23 @@ + + + + arex-instrumentation-parent + io.arex + 0.2.0 + ../../pom.xml + + 4.0.0 + + arex-integration-mqtt + + + + org.springframework.integration + spring-integration-mqtt + 5.5.10 + provided + + + diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java new file mode 100644 index 000000000..3b816a64f --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java @@ -0,0 +1,16 @@ +package io.arex.inst.mqtt; + +import io.arex.agent.bootstrap.model.Mocker; +import io.arex.inst.runtime.util.MockUtils; + +/** + * MQTTAdapterHelper + */ +public class MQTTAdapterHelper { + + public static Mocker createMocker(String operationName) { + Mocker mocker = MockUtils.createMqttConsumer(operationName); + mocker.getTargetRequest().setType(Byte.class.getName()); + return mocker; + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java new file mode 100644 index 000000000..94f13ce4f --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java @@ -0,0 +1,63 @@ +package io.arex.inst.mqtt.inst; + +import io.arex.agent.bootstrap.model.Mocker; +import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.inst.extension.MethodInstrumentation; +import io.arex.inst.extension.TypeInstrumentation; +import io.arex.inst.mqtt.MQTTAdapterHelper; +import io.arex.inst.runtime.context.ContextManager; +import io.arex.inst.runtime.util.MockUtils; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isInterface; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +/** + * EclipseInstrumentationV3 + */ +public class EclipseInstrumentationV3 extends TypeInstrumentation { + @Override + protected ElementMatcher typeMatcher() { + return not(isInterface()).and(hasSuperType(named("org.eclipse.paho.client.mqttv3.MqttCallback"))); + } + + @Override + public List methodAdvices() { + ElementMatcher matcher = named("messageArrived") + .and(takesArgument(0, named("java.lang.String"))) + .and(takesArgument(1, named("org.eclipse.paho.client.mqttv3.MqttMessage"))); + return Collections.singletonList(new MethodInstrumentation(matcher, ArrivedAdvice.class.getName())); + } + + public static class ArrivedAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(value = 0, readOnly = false) String topic, + @Advice.Argument(value = 1, readOnly = false) MqttMessage mqttMessage) { + if (StringUtil.isEmpty(topic) || mqttMessage == null) { + return; + } + if (ContextManager.needRecordOrReplay()){ + Mocker mocker = MQTTAdapterHelper.createMocker(topic); + mocker.getTargetRequest().setBody(Base64.getEncoder().encodeToString(mqttMessage.getPayload())); + if (ContextManager.needReplay()) { + MockUtils.replayMocker(mocker); + } else if (ContextManager.needRecord()) { + MockUtils.recordMocker(mocker); + } + } + } + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java new file mode 100644 index 000000000..ac079daef --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java @@ -0,0 +1,24 @@ +package io.arex.inst.mqtt.inst; + +import com.google.auto.service.AutoService; +import io.arex.inst.extension.ModuleInstrumentation; +import io.arex.inst.extension.TypeInstrumentation; + +import java.util.Arrays; +import java.util.List; + +/** + * MQTTAdapterModuleInstrumentation + */ +@AutoService(ModuleInstrumentation.class) +public class MQTTAdapterModuleInstrumentation extends ModuleInstrumentation { + + public MQTTAdapterModuleInstrumentation() { + super("mqtt-adapter"); + } + + @Override + public List instrumentationTypes() { + return Arrays.asList(new EclipseInstrumentationV3()); + } +} diff --git a/arex-instrumentation/pom.xml b/arex-instrumentation/pom.xml index f1f935c09..cd1f52aee 100644 --- a/arex-instrumentation/pom.xml +++ b/arex-instrumentation/pom.xml @@ -43,6 +43,7 @@ authentication/arex-shiro authentication/arex-jcasbin foundation/arex-serializer + mq/arex-integration-mqtt From cca9c6efe653c24b77ff1866fa01e42a51da8581 Mon Sep 17 00:00:00 2001 From: Montos <1367654518@qq.com> Date: Mon, 24 Apr 2023 15:53:00 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91+=E5=A2=9E=E5=8A=A0unitTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加多类型匹配 增加多类型匹配 修改泛型+unitTest 调整方案 --- .../convert/HttpMessageConvertFactory.java | 41 +++++++++ .../convert/HttpMessageConverter.java | 10 +++ .../impl/ApplicationJsonBodyConverter.java | 36 ++++++++ .../impl/ApplicationXmlBodyConverter.java | 31 +++++++ .../impl/DefaultHttpMessageConverter.java | 35 ++++++++ ...t.httpservlet.convert.HttpMessageConverter | 2 + .../HttpMessageConvertFactoryTest.java | 51 +++++++++++ .../impl/ApplicationJsonBodyConvertTest.java | 87 +++++++++++++++++++ .../impl/ApplicationXmlBodyConvertTest.java | 86 ++++++++++++++++++ ...ex.inst.httpservlet.convert.BodyConverters | 2 + 10 files changed, 381 insertions(+) create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactory.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConverter.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConverter.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConverter.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/DefaultHttpMessageConverter.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactoryTest.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConvertTest.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConvertTest.java create mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactory.java b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactory.java new file mode 100644 index 000000000..b305d27e6 --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactory.java @@ -0,0 +1,41 @@ +package io.arex.inst.httpservlet.convert; + +import io.arex.agent.bootstrap.util.CollectionUtil; +import io.arex.inst.httpservlet.adapter.ServletAdapter; +import io.arex.inst.httpservlet.convert.impl.DefaultHttpMessageConverter; + +import java.util.*; + +public class HttpMessageConvertFactory { + + + private static final List cacheList = new ArrayList<>(); + + + + static { + ServiceLoader load = ServiceLoader.load(HttpMessageConverter.class); + if (load != null) { + Iterator iterator = load.iterator(); + while (iterator.hasNext()) { + cacheList.add(iterator.next()); + } + } + } + + + public static HttpMessageConverter getSupportedConverter(P p, R r, ServletAdapter adapter) { + synchronized (HttpMessageConvertFactory.class) { + if (CollectionUtil.isNotEmpty(cacheList)) { + for (HttpMessageConverter converter : cacheList) { + if (converter.match(p, r, adapter)) { + return converter; + } + } + } + return DefaultHttpMessageConverter.getInstance(); + } + } + + +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConverter.java b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConverter.java new file mode 100644 index 000000000..52f2f94ea --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/HttpMessageConverter.java @@ -0,0 +1,10 @@ +package io.arex.inst.httpservlet.convert; + +import io.arex.inst.httpservlet.adapter.ServletAdapter; + +public interface HttpMessageConverter { + + boolean match(HttpServletRequest request,HttpServletResponse response, ServletAdapter adapter); + byte[] getRequest(HttpServletRequest request, ServletAdapter adapter); + byte[] getResponse(HttpServletResponse request, ServletAdapter adapter); +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConverter.java b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConverter.java new file mode 100644 index 000000000..4e5d760b8 --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConverter.java @@ -0,0 +1,36 @@ +package io.arex.inst.httpservlet.convert.impl; + +import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.inst.httpservlet.adapter.ServletAdapter; +import io.arex.inst.httpservlet.convert.HttpMessageConverter; + +public class ApplicationJsonBodyConverter implements HttpMessageConverter { + + private static final String CONTENT_TYPE = "application/json"; + + @Override + public boolean match(HttpServletRequest request, HttpServletResponse response, ServletAdapter adapter) { + if (request == null || response ==null || adapter == null) { + return false; + } + String contentType = adapter.getContentType(request); + return StringUtil.isNotEmpty(contentType) && contentType.contains(CONTENT_TYPE); + } + + @Override + public byte[] getRequest(HttpServletRequest request, ServletAdapter adapter) { + if (request == null || adapter == null) { + return new byte[0]; + } + return adapter.getRequestBytes(request); + } + + @Override + public byte[] getResponse(HttpServletResponse response, ServletAdapter adapter) { + return new byte[0]; + } +} + + + + diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConverter.java b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConverter.java new file mode 100644 index 000000000..aa84bd6d4 --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConverter.java @@ -0,0 +1,31 @@ +package io.arex.inst.httpservlet.convert.impl; + +import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.inst.httpservlet.adapter.ServletAdapter; +import io.arex.inst.httpservlet.convert.HttpMessageConverter; + + +public class ApplicationXmlBodyConverter implements HttpMessageConverter { + + private static final String CONTENT_TYPE = "application/xml"; + + @Override + public boolean match(HttpServletRequest request, HttpServletResponse response, ServletAdapter adapter) { + if (request == null || response ==null || adapter == null) { + return false; + } + String contentType = adapter.getContentType(request); + return StringUtil.isNotEmpty(contentType) && contentType.contains(CONTENT_TYPE); + } + + @Override + public byte[] getRequest(HttpServletRequest request, ServletAdapter adapter) { + return new byte[0]; + } + + @Override + public byte[] getResponse(HttpServletResponse response, ServletAdapter adapter) { + return new byte[0]; + } + +} \ No newline at end of file diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/DefaultHttpMessageConverter.java b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/DefaultHttpMessageConverter.java new file mode 100644 index 000000000..33ddf83ee --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/java/io/arex/inst/httpservlet/convert/impl/DefaultHttpMessageConverter.java @@ -0,0 +1,35 @@ +package io.arex.inst.httpservlet.convert.impl; + +import io.arex.inst.httpservlet.adapter.ServletAdapter; +import io.arex.inst.httpservlet.convert.HttpMessageConverter; + +public class DefaultHttpMessageConverter implements HttpMessageConverter { + + private DefaultHttpMessageConverter() { + + } + + private static class SingletonHolder { + private static final DefaultHttpMessageConverter INSTANCE = new DefaultHttpMessageConverter(); + } + + public static DefaultHttpMessageConverter getInstance() { + return SingletonHolder.INSTANCE; + } + + + @Override + public boolean match(HttpServletRequest request, HttpServletResponse response, ServletAdapter adapter) { + return false; + } + + @Override + public byte[] getRequest(HttpServletRequest request, ServletAdapter adapter) { + return new byte[0]; + } + + @Override + public byte[] getResponse(HttpServletResponse response, ServletAdapter adapter) { + return new byte[0]; + } +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter b/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter new file mode 100644 index 000000000..24bad6272 --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter @@ -0,0 +1,2 @@ +io.arex.inst.httpservlet.convert.impl.ApplicationJsonBodyConverter +io.arex.inst.httpservlet.convert.impl.ApplicationXmlBodyConverter \ No newline at end of file diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactoryTest.java b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactoryTest.java new file mode 100644 index 000000000..6778462f2 --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/HttpMessageConvertFactoryTest.java @@ -0,0 +1,51 @@ +package io.arex.inst.httpservlet.convert; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV3; +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV5; +import io.arex.inst.httpservlet.convert.impl.DefaultHttpMessageConverter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HttpMessageConvertFactoryTest { + + HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class); + HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class); + ServletAdapterImplV3 instance3 = ServletAdapterImplV3.getInstance(); + ServletAdapterImplV5 instance5 = ServletAdapterImplV5.getInstance(); + jakarta.servlet.http.HttpServletRequest mockRequest5 = Mockito.mock(jakarta.servlet.http.HttpServletRequest.class); + jakarta.servlet.http.HttpServletResponse mockResponse5 = Mockito.mock(jakarta.servlet.http.HttpServletResponse.class); + + @BeforeEach + void setUp() { + } + + @AfterEach + void tearDown() { + } + + @Test + void getOne(){ + when(mockRequest.getContentType()).thenReturn("application/json"); + when(mockRequest5.getContentType()).thenReturn("application/json"); + assertNotNull(HttpMessageConvertFactory.getSupportedConverter(mockRequest,mockResponse,instance3)); + assertNotNull(HttpMessageConvertFactory.getSupportedConverter(mockRequest5,mockResponse5,instance5)); + } + + @Test + void getDefault(){ + when(mockRequest.getContentType()).thenReturn("application/jn"); + when(mockRequest5.getContentType()).thenReturn("application/jn"); + + assertTrue(HttpMessageConvertFactory.getSupportedConverter(mockRequest,mockResponse,instance3) instanceof DefaultHttpMessageConverter); + assertTrue(HttpMessageConvertFactory.getSupportedConverter(mockRequest5,mockResponse5,instance5) instanceof DefaultHttpMessageConverter); + } + +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConvertTest.java b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConvertTest.java new file mode 100644 index 000000000..38830c02f --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationJsonBodyConvertTest.java @@ -0,0 +1,87 @@ +package io.arex.inst.httpservlet.convert.impl; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + + +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV3; +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV5; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class ApplicationJsonBodyConvertTest { + + HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class); + HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class); + + ServletAdapterImplV3 instance3 = ServletAdapterImplV3.getInstance(); + ServletAdapterImplV5 instance5 = ServletAdapterImplV5.getInstance(); + jakarta.servlet.http.HttpServletRequest mockRequest5 = Mockito.mock(jakarta.servlet.http.HttpServletRequest.class); + jakarta.servlet.http.HttpServletResponse mockResponse5 = Mockito.mock(jakarta.servlet.http.HttpServletResponse.class); + + + static ApplicationJsonBodyConverter applicationJsonBodyConvert = null; + + + + @BeforeEach + void setUp() { + applicationJsonBodyConvert = new ApplicationJsonBodyConverter(); + + } + + @AfterEach + void tearDown() { + } + + @Test + void match() { + when(mockRequest.getContentType()).thenReturn("application/json"); + when(mockRequest5.getContentType()).thenReturn("application/json"); + assertEquals(true, applicationJsonBodyConvert.match(mockRequest,mockResponse,instance3)); + assertEquals(true, applicationJsonBodyConvert.match(mockRequest5,mockResponse5,instance5)); + } + + + @Test + void notMatch1() { + when(mockRequest.getContentType()).thenReturn("application/jn"); + when(mockRequest5.getContentType()).thenReturn("application/n"); + assertEquals(false, applicationJsonBodyConvert.match(mockRequest,mockResponse,instance3)); + assertEquals(false, applicationJsonBodyConvert.match(mockRequest5,mockResponse5,instance5)); + } + + + @Test + void notMatch2() { + assertEquals(false, applicationJsonBodyConvert.match(null,mockResponse,instance3)); + assertEquals(false, applicationJsonBodyConvert.match(null,mockResponse5,instance5)); + } + + + @Test + void get0Request() { + assertEquals(0, applicationJsonBodyConvert.getRequest(null,instance3).length); + assertEquals(0, applicationJsonBodyConvert.getRequest(null,instance5).length); + } + + @Test + void getRequest() { + assertEquals(0, applicationJsonBodyConvert.getRequest(instance3.wrapRequest(mockRequest),instance3).length); + assertEquals(0, applicationJsonBodyConvert.getRequest(instance5.wrapRequest(mockRequest5),instance5).length); + } + + + @Test + void getResponse() { + assertEquals(0, applicationJsonBodyConvert.getResponse(instance3.wrapResponse(mockResponse),instance3).length); + assertEquals(0, applicationJsonBodyConvert.getResponse(instance5.wrapResponse(mockResponse5),instance5).length); + } + +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConvertTest.java b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConvertTest.java new file mode 100644 index 000000000..09cb0276f --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/test/java/io/arex/inst/httpservlet/convert/impl/ApplicationXmlBodyConvertTest.java @@ -0,0 +1,86 @@ +package io.arex.inst.httpservlet.convert.impl; + +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV3; +import io.arex.inst.httpservlet.adapter.impl.ServletAdapterImplV5; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.when; + +public class ApplicationXmlBodyConvertTest { + + HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class); + HttpServletResponse mockResponse = Mockito.mock(HttpServletResponse.class); + + ServletAdapterImplV3 instance3 = ServletAdapterImplV3.getInstance(); + ServletAdapterImplV5 instance5 = ServletAdapterImplV5.getInstance(); + jakarta.servlet.http.HttpServletRequest mockRequest5 = Mockito.mock(jakarta.servlet.http.HttpServletRequest.class); + jakarta.servlet.http.HttpServletResponse mockResponse5 = Mockito.mock(jakarta.servlet.http.HttpServletResponse.class); + + + static ApplicationXmlBodyConverter applicationXmlBodyConvert = null; + + + + @BeforeEach + void setUp() { + applicationXmlBodyConvert = new ApplicationXmlBodyConverter(); + + } + + @AfterEach + void tearDown() { + } + + @Test + void match() { + when(mockRequest.getContentType()).thenReturn("application/xml"); + when(mockRequest5.getContentType()).thenReturn("application/xml"); + assertEquals(true, applicationXmlBodyConvert.match(mockRequest,mockResponse,instance3)); + assertEquals(true, applicationXmlBodyConvert.match(mockRequest5,mockResponse5,instance5)); + } + + + @Test + void notMatch1() { + when(mockRequest.getContentType()).thenReturn("application/xl"); + when(mockRequest5.getContentType()).thenReturn("application/xl"); + assertEquals(false, applicationXmlBodyConvert.match(mockRequest,mockResponse,instance3)); + assertEquals(false, applicationXmlBodyConvert.match(mockRequest5,mockResponse5,instance5)); + } + + + @Test + void notMatch2() { + assertEquals(false, applicationXmlBodyConvert.match(null,mockResponse,instance3)); + assertEquals(false, applicationXmlBodyConvert.match(null,mockResponse5,instance5)); + } + + + @Test + void get0Request() { + assertEquals(0, applicationXmlBodyConvert.getRequest(null,instance3).length); + assertEquals(0, applicationXmlBodyConvert.getRequest(null,instance5).length); + } + + @Test + void getRequest() { + assertEquals(0, applicationXmlBodyConvert.getRequest(instance3.wrapRequest(mockRequest),instance3).length); + assertEquals(0, applicationXmlBodyConvert.getRequest(instance5.wrapRequest(mockRequest5),instance5).length); + } + + + @Test + void getResponse() { + assertEquals(0, applicationXmlBodyConvert.getResponse(instance3.wrapResponse(mockResponse),instance3).length); + assertEquals(0, applicationXmlBodyConvert.getResponse(instance5.wrapResponse(mockResponse5),instance5).length); + } + +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters b/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters new file mode 100644 index 000000000..4937f54ab --- /dev/null +++ b/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters @@ -0,0 +1,2 @@ +io.arex.inst.httpservlet.convert.impl.ApplicationJsonBodyConvert +io.arex.inst.httpservlet.convert.impl.ApplicationXmlBodyConvert \ No newline at end of file From 72aac4e286fc3c7c84a4c2474ad565677a1d57c1 Mon Sep 17 00:00:00 2001 From: Montos <1367654518@qq.com> Date: Thu, 27 Apr 2023 18:45:20 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=85=B6=E4=BB=96task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复tkmybatis序列化循环引用 修复tkmybatis序列化循环引用 去除其他task --- .../java/io/arex/foundation/serializer/JacksonSerializer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java b/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java index 2be504b8f..2fd90d476 100644 --- a/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java +++ b/arex-instrumentation-foundation/src/main/java/io/arex/foundation/serializer/JacksonSerializer.java @@ -23,7 +23,6 @@ import org.apache.commons.lang3.time.FastDateFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tk.mybatis.mapper.entity.EntityTable; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; @@ -169,8 +168,6 @@ private void configMapper() { MAPPER.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); MAPPER.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); - - MAPPER.addMixIn(EntityTable.class,IgnoreType.class); } private void customTimeFormatSerializer(SimpleModule module) { From 5321bdc5253fc86fc27f148046a26413eaa68dfe Mon Sep 17 00:00:00 2001 From: MentosL <1367654518@qq.com> Date: Sun, 7 May 2023 23:38:39 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0mqtt=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加mqtt相关代码逻辑 Update pom.xml 新增mqtt 新增mqtt组件 调整 提交 Update MessageHeaderWarp.java 提交warp 增加mqtt代码 提交代码 提交部分代码 Delete io.arex.inst.httpservlet.convert.HttpMessageConverter --- .../mq/arex-integration-mqtt/pom.xml | 8 +- .../io/arex/inst/mqtt/MQTTAdapterHelper.java | 79 ++++++++- .../arex/inst/mqtt/MessageQueueExtractor.java | 78 ++++++++ .../inst/mqtt/adapter/MessageAdapter.java | 28 +++ .../inst/mqtt/adapter/MessageAdapterImpl.java | 166 ++++++++++++++++++ .../mqtt/inst/EclipseInstrumentationV3.java | 63 ------- .../mqtt/inst/MQGenericInstrumentationV3.java | 66 +++++++ .../MQTTAdapterModuleInstrumentation.java | 4 +- .../inst/mqtt/warp/GenericMessageWarp.java | 53 ++++++ .../inst/mqtt/warp/MessageHeaderWarp.java | 47 +++++ .../inst/mqtt/inst/MQTTAdapterHelperTest.java | 106 +++++++++++ .../mqtt/inst/MessageQueueExtractorTest.java | 115 ++++++++++++ .../inst/adapter/impl/MessageAdapterTest.java | 109 ++++++++++++ .../inst/MQGenericInstrumentationV3Test.java | 65 +++++++ .../MQTTAdapterModuleInstrumentationTest.java | 30 ++++ .../inst/warp/GenericMessageWarpTest.java | 65 +++++++ .../mqtt/inst/warp/MessageHeaderWarpTest.java | 54 ++++++ ...t.httpservlet.convert.HttpMessageConverter | 2 - ...ex.inst.httpservlet.convert.BodyConverters | 2 - 19 files changed, 1061 insertions(+), 79 deletions(-) create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MessageQueueExtractor.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapter.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapterImpl.java delete mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQGenericInstrumentationV3.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/GenericMessageWarp.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/MessageHeaderWarp.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MQTTAdapterHelperTest.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MessageQueueExtractorTest.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/adapter/impl/MessageAdapterTest.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQGenericInstrumentationV3Test.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQTTAdapterModuleInstrumentationTest.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/GenericMessageWarpTest.java create mode 100644 arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/MessageHeaderWarpTest.java delete mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter delete mode 100644 arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters diff --git a/arex-instrumentation/mq/arex-integration-mqtt/pom.xml b/arex-instrumentation/mq/arex-integration-mqtt/pom.xml index 6e84a2f46..92bf444c5 100644 --- a/arex-instrumentation/mq/arex-integration-mqtt/pom.xml +++ b/arex-instrumentation/mq/arex-integration-mqtt/pom.xml @@ -5,7 +5,7 @@ arex-instrumentation-parent io.arex - 0.2.0 + ${revision} ../../pom.xml 4.0.0 @@ -14,9 +14,9 @@ - org.springframework.integration - spring-integration-mqtt - 5.5.10 + org.springframework + spring-messaging + 5.3.4 provided diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java index 3b816a64f..622a5e87c 100644 --- a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MQTTAdapterHelper.java @@ -1,16 +1,83 @@ package io.arex.inst.mqtt; -import io.arex.agent.bootstrap.model.Mocker; -import io.arex.inst.runtime.util.MockUtils; +import io.arex.agent.bootstrap.internal.Pair; +import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.inst.mqtt.adapter.MessageAdapter; +import io.arex.inst.runtime.config.Config; +import io.arex.inst.runtime.listener.CaseEvent; +import io.arex.inst.runtime.listener.CaseEventDispatcher; +import io.arex.inst.runtime.listener.EventSource; +import io.arex.inst.runtime.model.ArexConstants; +import io.arex.inst.runtime.util.IgnoreUtils; /** * MQTTAdapterHelper */ public class MQTTAdapterHelper { - public static Mocker createMocker(String operationName) { - Mocker mocker = MockUtils.createMqttConsumer(operationName); - mocker.getTargetRequest().setType(Byte.class.getName()); - return mocker; + public static final String PROCESSED_FLAG = "arex-processed-flag"; + + + public static Pair onServiceEnter(MessageAdapter adapter, Object messageChannel, Object message) { + Msg msg = adapter.warpMessage(message); + if (msg == null) { + return null; + } + if (adapter.markProcessed(msg, PROCESSED_FLAG)) { + return null; + } + MC mc = adapter.warpMC(messageChannel); + if (mc == null){ + return null; + } + CaseEventDispatcher.onEvent(CaseEvent.ofEnterEvent()); + if (shouldSkip(adapter, mc, msg)) { + return null; + } + String caseId = adapter.getHeader(mc, msg, ArexConstants.RECORD_ID); + String excludeMockTemplate = adapter.getHeader(mc, msg, ArexConstants.HEADER_EXCLUDE_MOCK); + CaseEventDispatcher.onEvent(CaseEvent.ofCreateEvent(EventSource.of(caseId, excludeMockTemplate))); + return Pair.of(mc,msg); + } + + + public static void onServiceExit(MessageAdapter adapter, Object messageChannel, Object message){ + Msg msg = adapter.warpMessage(message); + MC mc = adapter.warpMC(messageChannel); + if (msg == null || mc == null) { + return; + } + adapter.removeHeader(mc,msg,PROCESSED_FLAG); + new MessageQueueExtractor<>( mc, msg,adapter).execute(); + } + + + + + private static boolean shouldSkip(MessageAdapter adapter,MC mc, Msg msg){ + String caseId = adapter.getHeader(mc, msg, ArexConstants.RECORD_ID); + // Replay scene + if (StringUtil.isNotEmpty(caseId)) { + return Config.get().getBoolean("arex.disable.replay", false); + } + + String forceRecord = adapter.getHeader(mc, msg, ArexConstants.FORCE_RECORD); + // Do not skip if header with arex-force-record=true + if (Boolean.parseBoolean(forceRecord)) { + return false; + } + // Skip if request header with arex-replay-warm-up=true + if (Boolean.parseBoolean(adapter.getHeader(mc, msg, ArexConstants.REPLAY_WARM_UP))) { + return true; + } + String topic = adapter.getHeader(mc, msg, ArexConstants.REPLAY_WARM_UP); + if (StringUtil.isEmpty(topic)) { + return false; + } + if (IgnoreUtils.ignoreOperation(topic)) { + return true; + } + return Config.get().invalidRecord(topic); } + } diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MessageQueueExtractor.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MessageQueueExtractor.java new file mode 100644 index 000000000..fd2c79fce --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/MessageQueueExtractor.java @@ -0,0 +1,78 @@ +package io.arex.inst.mqtt; + +import io.arex.agent.bootstrap.model.Mocker; +import io.arex.inst.mqtt.adapter.MessageAdapter; +import io.arex.inst.runtime.context.ContextManager; +import io.arex.inst.runtime.model.ArexConstants; +import io.arex.inst.runtime.util.LogUtil; +import io.arex.inst.runtime.util.MockUtils; +import org.springframework.messaging.MessageHeaders; + +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @author : MentosL + * @date : 2023/5/9 22:16 + */ +public class MessageQueueExtractor { + private final MC messageChannel; + private final Msg message; + private final MessageAdapter adapter; + + + public MessageQueueExtractor(MC messageChannel, Msg message, MessageAdapter adapter) { + this.messageChannel = messageChannel; + this.message = message; + this.adapter = adapter; + } + + + public void execute() { + try { + if (message == null || messageChannel == null || adapter == null) { + return; + } + if (!ContextManager.needRecordOrReplay()) { + return; + } + executeBeforeProcess(); + doExecute(); + executeAfterProcess(); + } catch (Exception e) { + LogUtil.warn("MessageQueue.execute", e); + } + } + + private void executeBeforeProcess() { + if (ContextManager.needRecord()) { + adapter.addHeader(messageChannel,message, ArexConstants.RECORD_ID,ContextManager.currentContext().getCaseId()); + } + if (ContextManager.needReplay()) { + adapter.addHeader(messageChannel,message, ArexConstants.REPLAY_ID,ContextManager.currentContext().getReplayId()); + } + } + private void executeAfterProcess(){ + // Think about other ways to replace the head + adapter.resetMsg(message); + } + + private void doExecute() { + Mocker mocker = MockUtils.createMqttConsumer(adapter.getHeader(messageChannel,message,"mqtt_receivedTopic")); + MessageHeaders header = adapter.getHeader(messageChannel, message); + Map requestOrigin = new HashMap<>(); + for (Map.Entry entry : header.entrySet()) { + requestOrigin.put(entry.getKey(), entry.getValue()); + } + Map requestAttributes = Collections.singletonMap("Headers", requestOrigin); + mocker.getTargetRequest().setAttributes(requestAttributes); + mocker.getTargetRequest().setBody(Base64.getEncoder().encodeToString(adapter.getMsg(messageChannel,message))); + if (ContextManager.needReplay()) { + MockUtils.replayMocker(mocker); + } else if (ContextManager.needRecord()) { + MockUtils.recordMocker(mocker); + } + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapter.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapter.java new file mode 100644 index 000000000..c573aadf7 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapter.java @@ -0,0 +1,28 @@ +package io.arex.inst.mqtt.adapter; + +import org.springframework.messaging.MessageHeaders; + +/** + * MessageAdapter + */ +public interface MessageAdapter { + + MC warpMC(Object messageChannel); + + Msg warpMessage(Object message); + + byte[] getMsg(MC c, Msg msg); + + MessageHeaders getHeader(MC c, Msg msg); + + boolean markProcessed(Msg msg,String flagKey); + + String getHeader(MC mc,Msg msg,String key); + + boolean removeHeader(MC mc,Msg msg,String key); + + boolean addHeader(MC mc,Msg msg,String key,String value); + + Msg resetMsg(Msg msg); + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapterImpl.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapterImpl.java new file mode 100644 index 000000000..79bd509fb --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/adapter/MessageAdapterImpl.java @@ -0,0 +1,166 @@ +package io.arex.inst.mqtt.adapter; + +import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.inst.mqtt.warp.GenericMessageWarp; +import io.arex.inst.mqtt.warp.MessageHeaderWarp; +import io.arex.inst.runtime.util.LogUtil; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; + +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; + +/** + * MessageImpl + */ +public class MessageAdapterImpl implements MessageAdapter { + + private static final MessageAdapterImpl INSTANCE = new MessageAdapterImpl(); + + public static MessageAdapterImpl getInstance() { + return INSTANCE; + } + + @Override + public byte[] getMsg(MessageChannel messageChannel, Message msg) { + if (msg == null){ + return new byte[]{}; + } + Object payload = msg.getPayload(); + if (payload == null){ + return new byte[]{}; + } + if (payload instanceof byte[]){ + return ((byte[]) payload); + } + return payload.toString().getBytes(StandardCharsets.UTF_8); + } + + @Override + public MessageChannel warpMC(Object messageChannel) { + if (messageChannel == null){ + return null; + } + if (messageChannel instanceof MessageChannel){ + return (MessageChannel) messageChannel; + } + return null; + } + + @Override + public Message warpMessage(Object message) { + if (message == null){ + return null; + } + if (message instanceof GenericMessageWarp){ + return (GenericMessageWarp) message; + } + + if (message instanceof GenericMessage) { + GenericMessage messageTemp = (GenericMessage) message; + MessageHeaders headers = messageTemp.getHeaders(); + MessageHeaderWarp messageHeaderWarp = new MessageHeaderWarp(headers); + return new GenericMessageWarp(messageTemp.getPayload(), messageHeaderWarp); + } + if (message instanceof Message){ + return (Message)message; + } + return null; + } + + @Override + public MessageHeaders getHeader(MessageChannel messageChannel, Message msg) { + if (msg == null){ + return null; + } + if (msg instanceof GenericMessageWarp){ + GenericMessageWarp messageTemp = (GenericMessageWarp) msg; + return messageTemp.getMessageHeaderWarp(); + } + return msg.getHeaders(); + } + + @Override + public boolean markProcessed(Message message, String flagKey) { + if (message == null){ + return true; + } + if (message instanceof GenericMessageWarp){ + GenericMessageWarp genericMessageWarp = (GenericMessageWarp)message; + genericMessageWarp.put(flagKey,Boolean.TRUE.toString()); + } + return false; + } + + @Override + public String getHeader(MessageChannel messageChannel, Message message, String key) { + if (message == null || StringUtil.isEmpty(key)){ + return null; + } + if (message instanceof GenericMessageWarp) { + GenericMessageWarp genericMessageWarp = (GenericMessageWarp) message; + Object object = genericMessageWarp.get(key); + return object != null ? object.toString() : null; + } + + if(message instanceof GenericMessage){ + Object obj = message.getHeaders().get(key); + return obj != null ? obj.toString() : null; + } + if (message.getHeaders() != null){ + Object obj = message.getHeaders().get(key); + return obj != null ? obj.toString() : null ; + } + return null; + } + + @Override + public boolean removeHeader(MessageChannel messageChannel, Message message, String key) { + if (message == null || StringUtil.isEmpty(key)){ + return false; + } + if (message instanceof GenericMessageWarp){ + GenericMessageWarp genericMessageWarp = (GenericMessageWarp) message; + genericMessageWarp.removeHeader(key); + return true; + } + return false; + } + + @Override + public boolean addHeader(MessageChannel messageChannel, Message message, String key, String value) { + if (message == null ){ + return false; + } + if (message instanceof GenericMessageWarp){ + GenericMessageWarp genericMessageWarp = (GenericMessageWarp) message; + genericMessageWarp.put(key,value); + return true; + } + return false; + } + + @Override + public Message resetMsg(Message message) { + if (message == null){ + return null; + } + if (message instanceof GenericMessageWarp){ + try { + GenericMessageWarp messageWarp = (GenericMessageWarp) message; + Field headers = message.getClass().getSuperclass().getDeclaredField("headers"); + headers.setAccessible(true); + headers.set(message, messageWarp.getMessageHeaderWarp()); + } catch (NoSuchFieldException e) { + LogUtil.warn("MessageAdapterImpl.resetMsg - NoSuchFieldException", e); + } catch (IllegalAccessException e) { + LogUtil.warn("MessageAdapterImpl.resetMsg - IllegalAccessException", e); + } + } + return message; + } + + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java deleted file mode 100644 index 94f13ce4f..000000000 --- a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/EclipseInstrumentationV3.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.arex.inst.mqtt.inst; - -import io.arex.agent.bootstrap.model.Mocker; -import io.arex.agent.bootstrap.util.StringUtil; -import io.arex.inst.extension.MethodInstrumentation; -import io.arex.inst.extension.TypeInstrumentation; -import io.arex.inst.mqtt.MQTTAdapterHelper; -import io.arex.inst.runtime.context.ContextManager; -import io.arex.inst.runtime.util.MockUtils; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -import java.util.Base64; -import java.util.Collections; -import java.util.List; - -import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; -import static net.bytebuddy.matcher.ElementMatchers.isInterface; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; - -/** - * EclipseInstrumentationV3 - */ -public class EclipseInstrumentationV3 extends TypeInstrumentation { - @Override - protected ElementMatcher typeMatcher() { - return not(isInterface()).and(hasSuperType(named("org.eclipse.paho.client.mqttv3.MqttCallback"))); - } - - @Override - public List methodAdvices() { - ElementMatcher matcher = named("messageArrived") - .and(takesArgument(0, named("java.lang.String"))) - .and(takesArgument(1, named("org.eclipse.paho.client.mqttv3.MqttMessage"))); - return Collections.singletonList(new MethodInstrumentation(matcher, ArrivedAdvice.class.getName())); - } - - public static class ArrivedAdvice { - - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnter(@Advice.Argument(value = 0, readOnly = false) String topic, - @Advice.Argument(value = 1, readOnly = false) MqttMessage mqttMessage) { - if (StringUtil.isEmpty(topic) || mqttMessage == null) { - return; - } - if (ContextManager.needRecordOrReplay()){ - Mocker mocker = MQTTAdapterHelper.createMocker(topic); - mocker.getTargetRequest().setBody(Base64.getEncoder().encodeToString(mqttMessage.getPayload())); - if (ContextManager.needReplay()) { - MockUtils.replayMocker(mocker); - } else if (ContextManager.needRecord()) { - MockUtils.recordMocker(mocker); - } - } - } - } - -} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQGenericInstrumentationV3.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQGenericInstrumentationV3.java new file mode 100644 index 000000000..92656bc56 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQGenericInstrumentationV3.java @@ -0,0 +1,66 @@ +package io.arex.inst.mqtt.inst; + +import io.arex.agent.bootstrap.internal.Pair; +import io.arex.inst.extension.MethodInstrumentation; +import io.arex.inst.extension.TypeInstrumentation; +import io.arex.inst.mqtt.MQTTAdapterHelper; +import io.arex.inst.mqtt.adapter.MessageAdapterImpl; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +import java.util.Collections; +import java.util.List; + +import static io.arex.inst.extension.matcher.SafeExtendsClassMatcher.extendsClass; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +/** + * EclipseInstrumentationV3 + */ +public class MQGenericInstrumentationV3 extends TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("org.springframework.messaging.core.AbstractMessageSendingTemplate"), false) + .and(named("org.springframework.messaging.core.GenericMessagingTemplate")); + } + + @Override + public List methodAdvices() { + ElementMatcher matcher = named("doSend") + .and(takesArgument(0, named("org.springframework.messaging.MessageChannel"))) + .and(takesArgument(1, named("org.springframework.messaging.Message"))); + return Collections.singletonList(new MethodInstrumentation(matcher, ArrivedAdvice.class.getName())); + } + + public static class ArrivedAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(value = 0, readOnly = false) MessageChannel messageChannel, + @Advice.Argument(value = 1, readOnly = false) Message message, + @Advice.Local("channelMsgPair") Pair pair) { + Pair messageChannelMessagePair = + MQTTAdapterHelper.onServiceEnter(MessageAdapterImpl.getInstance(), messageChannel, message); + if (messageChannelMessagePair == null){ + return; + } + pair = messageChannelMessagePair; + } + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.Argument(value = 0, readOnly = false) MessageChannel messageChannel, + @Advice.Argument(value = 1, readOnly = false) Message message, + @Advice.Local("channelMsgPair") Pair pair) { + if (pair != null){ + message = pair.getSecond(); + } + MQTTAdapterHelper.onServiceExit(MessageAdapterImpl.getInstance(), messageChannel, message); + } + + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java index ac079daef..6c3aaa6ed 100644 --- a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/inst/MQTTAdapterModuleInstrumentation.java @@ -14,11 +14,11 @@ public class MQTTAdapterModuleInstrumentation extends ModuleInstrumentation { public MQTTAdapterModuleInstrumentation() { - super("mqtt-adapter"); + super("mqtt"); } @Override public List instrumentationTypes() { - return Arrays.asList(new EclipseInstrumentationV3()); + return Arrays.asList(new MQGenericInstrumentationV3()); } } diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/GenericMessageWarp.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/GenericMessageWarp.java new file mode 100644 index 000000000..7b509411c --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/GenericMessageWarp.java @@ -0,0 +1,53 @@ +package io.arex.inst.mqtt.warp; + +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; + +import java.util.Map; + +/** + * @author : MentosL + * @date : 2023/5/10 20:53 + */ +public class GenericMessageWarp extends GenericMessage { + + private MessageHeaderWarp messageHeaderWarp; + + public GenericMessageWarp(Object payload) { + super(payload); + this.messageHeaderWarp = new MessageHeaderWarp(super.getHeaders()); + } + + public GenericMessageWarp(Object payload, Map headers) { + super(payload, headers); + this.messageHeaderWarp = new MessageHeaderWarp(headers); + } + + public GenericMessageWarp(Object payload, MessageHeaders headers) { + super(payload, headers); + this.messageHeaderWarp = new MessageHeaderWarp(headers); + } + + public void removeHeader(String key) { + if (this.messageHeaderWarp != null){ + this.messageHeaderWarp.remove(key); + } + } + + public void put(String key, String value) { + if (this.messageHeaderWarp != null) { + this.messageHeaderWarp.put(key, value); + } + } + + public Object get(String key){ + if (this.messageHeaderWarp != null) { + return this.messageHeaderWarp.get(key); + } + return getHeaders().get(key); + } + + public MessageHeaderWarp getMessageHeaderWarp() { + return messageHeaderWarp; + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/MessageHeaderWarp.java b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/MessageHeaderWarp.java new file mode 100644 index 000000000..a8284bfa4 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/main/java/io/arex/inst/mqtt/warp/MessageHeaderWarp.java @@ -0,0 +1,47 @@ +package io.arex.inst.mqtt.warp; + +import org.springframework.messaging.MessageHeaders; + +import java.util.Map; +import java.util.UUID; + +/** + * @author : MentosL + * @date : 2023/5/9 23:12 + */ +public class MessageHeaderWarp extends MessageHeaders { + + + public MessageHeaderWarp(MessageHeaders messageHeaders) { + super(messageHeaders); + if (messageHeaders != null && messageHeaders.size() > 0){ + if (messageHeaders.get(ID) != null){ + this.put(ID,messageHeaders.get(ID)); + } + if (messageHeaders.get(TIMESTAMP) != null){ + this.put(TIMESTAMP,messageHeaders.get(TIMESTAMP)); + } + } + } + + public MessageHeaderWarp(Map headers) { + super(headers); + } + + public MessageHeaderWarp(Map headers, UUID id, Long timestamp) { + super(headers, id, timestamp); + } + + public Object put(String key, Object value){ + if (value == null){ + return null; + } + super.getRawHeaders().put(key,value); + return value; + } + + public void remove(String key){ + super.getRawHeaders().remove(key); + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MQTTAdapterHelperTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MQTTAdapterHelperTest.java new file mode 100644 index 000000000..8b3aba92a --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MQTTAdapterHelperTest.java @@ -0,0 +1,106 @@ +package io.arex.inst.mqtt.inst; + +import io.arex.agent.bootstrap.internal.Pair; +import io.arex.inst.mqtt.MQTTAdapterHelper; +import io.arex.inst.mqtt.adapter.MessageAdapter; +import io.arex.inst.mqtt.adapter.MessageAdapterImpl; +import io.arex.inst.runtime.context.ContextManager; +import io.arex.inst.runtime.context.RecordLimiter; +import io.arex.inst.runtime.model.ArexConstants; +import io.arex.inst.runtime.util.IgnoreUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; + +/** + * @author : MentosL + * @date : 2023/5/16 20:53 + */ +@ExtendWith(MockitoExtension.class) +public class MQTTAdapterHelperTest { + static MessageAdapter messageAdapter; + static MessageChannel messageChannel; + static Message message; + + @BeforeAll + static void setUp() { + messageAdapter = Mockito.mock(MessageAdapter.class); + messageChannel = Mockito.mock(MessageChannel.class); + message = Mockito.mock(Message.class); + } + + @AfterAll + static void tearDown() { + messageAdapter = null; + messageChannel = null; + message = null; + Mockito.clearAllCaches(); + } + + @ParameterizedTest + @MethodSource("onServiceEnterCase") + void onServiceEnter(Runnable mocker, Predicate predicate) { + mocker.run(); + Pair result = MQTTAdapterHelper.onServiceEnter(messageAdapter, new Object(), new Object()); + assertTrue(predicate.test(result)); + } + + static Stream onServiceEnterCase() { + Runnable emptyMocker = () -> {}; + Runnable mocker1 = () -> { + Mockito.when(messageAdapter.warpMessage(any())).thenReturn("mock"); + Mockito.when(messageAdapter.markProcessed(any(), any())).thenReturn(true); + }; + Runnable mocker2 = () -> { + Mockito.when(messageAdapter.markProcessed(any(), any())).thenReturn(false); + }; + Runnable mocker3 = () -> { + Mockito.when(messageAdapter.getMsg(any(),any())).thenReturn(null); + }; + + Runnable mocker4 = () -> { + Mockito.when(messageAdapter.getHeader(any(), any())).thenReturn(null); + }; + Runnable mocker5 = () -> { + Mockito.when(messageAdapter.getHeader(any(), any(),eq(ArexConstants.RECORD_ID))).thenReturn("mock"); + }; + Runnable mocker6 = () -> { + Mockito.when(messageAdapter.removeHeader(any(), any(),eq(ArexConstants.RECORD_ID))).thenReturn(true); + }; + Runnable mocker7 = () -> { + Mockito.when(messageAdapter.addHeader(any(), any(),eq(ArexConstants.RECORD_ID),eq("mock"))).thenReturn(true); + }; + Runnable mocker8 = () -> { + Mockito.when(messageAdapter.resetMsg(any())).thenReturn("mock"); + }; + Predicate> predicate1 = Objects::isNull; + return Stream.of( + arguments(emptyMocker, predicate1), + arguments(mocker1, predicate1), + arguments(mocker2, predicate1), + arguments(mocker3, predicate1), + arguments(mocker4, predicate1), + arguments(mocker5, predicate1), + arguments(mocker6, predicate1), + arguments(mocker7, predicate1), + arguments(mocker8, predicate1) + ); + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MessageQueueExtractorTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MessageQueueExtractorTest.java new file mode 100644 index 000000000..57856b682 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/MessageQueueExtractorTest.java @@ -0,0 +1,115 @@ +package io.arex.inst.mqtt.inst; + +import io.arex.agent.bootstrap.model.ArexMocker; +import io.arex.agent.bootstrap.model.Mocker; +import io.arex.inst.mqtt.MessageQueueExtractor; +import io.arex.inst.mqtt.adapter.MessageAdapter; +import io.arex.inst.runtime.context.ArexContext; +import io.arex.inst.runtime.context.ContextManager; +import io.arex.inst.runtime.model.ArexConstants; +import io.arex.inst.runtime.util.MockUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +import java.io.IOException; +import java.util.Collections; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.mockito.ArgumentMatchers.any; + +/** + * @author : MentosL + * @date : 2023/5/22 10:27 + */ +public class MessageQueueExtractorTest { + static MessageAdapter adapter; + static Message message; + static MessageChannel messageChannel; + + @BeforeAll + static void setUp() { + adapter = Mockito.mock(MessageAdapter.class); + message = Mockito.mock(Message.class); + messageChannel = Mockito.mock(MessageChannel.class); + Mockito.mockStatic(ContextManager.class); + Mockito.mockStatic(MockUtils.class); + } + + @AfterAll + static void tearDown() { + adapter = null; + message = null; + messageChannel = null; + Mockito.clearAllCaches(); + } + + + + @ParameterizedTest(name = "[{index}] {2}") + @MethodSource("executeCase") + void execute(String log, Runnable mock, Runnable verify) throws IOException { + mock.run(); + new MessageQueueExtractor<>(messageChannel,message, adapter).execute(); + assertDoesNotThrow(verify::run); + } + + + static Stream executeCase() { + Runnable mock1 = () -> Mockito.when(adapter.addHeader(messageChannel,message, ArexConstants.REPLAY_ID,"mock-replay-id")).thenReturn(true); + + Runnable verify1 = () -> { + try { + adapter.resetMsg(message); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + + Runnable mock2 = () -> { + Mockito.when(adapter.getHeader(messageChannel,message, ArexConstants.REPLAY_ID)).thenReturn(null); + Mockito.when(ContextManager.needRecordOrReplay()).thenReturn(false); + }; + + Runnable mock3 = () -> { + Mockito.when(adapter.getHeader(messageChannel,message, ArexConstants.REPLAY_ID)).thenReturn(null); + Mockito.when(ContextManager.needRecordOrReplay()).thenReturn(true); + + ArexMocker mocker = new ArexMocker(); + mocker.setTargetRequest(new Mocker.Target()); + mocker.setTargetResponse(new Mocker.Target()); + Mockito.when(MockUtils.createServlet(any())).thenReturn(mocker); + + Mockito.when(adapter.getMsg(messageChannel,message)).thenReturn(new byte[0]); + Mockito.when(ContextManager.needRecord()).thenReturn(true); + Mockito.when(ContextManager.currentContext()).thenReturn(ArexContext.of("mock-trace-id")); + }; + Runnable verify2 = () -> { + Mockito.verify(adapter).addHeader(messageChannel,message, ArexConstants.RECORD_ID, "mock-trace-id"); + }; + + Runnable mock4 = () -> { + Mockito.when(ContextManager.needRecord()).thenReturn(false); + Mockito.when(ContextManager.needReplay()).thenReturn(true); + }; + + Runnable verify3 = () -> { + Mockito.verify(adapter).addHeader(messageChannel,message, ArexConstants.REPLAY_ID, null); + }; + + return Stream.of( + arguments("response header contains arex trace", mock1, verify1), + arguments("no need record or replay", mock2, verify1), + arguments("record execute", mock3, verify2), + arguments("replay execute", mock4, verify3) + ); + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/adapter/impl/MessageAdapterTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/adapter/impl/MessageAdapterTest.java new file mode 100644 index 000000000..0e4a831a5 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/adapter/impl/MessageAdapterTest.java @@ -0,0 +1,109 @@ +package io.arex.inst.mqtt.inst.adapter.impl; + +import io.arex.inst.mqtt.adapter.MessageAdapterImpl; +import io.arex.inst.mqtt.warp.GenericMessageWarp; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +/** + * @author : MentosL + * @date : 2023/5/11 16:27 + */ +public class MessageAdapterTest { + + MessageAdapterImpl instance = MessageAdapterImpl.getInstance(); + MessageChannel messageChannel = Mockito.mock(MessageChannel.class); + Message message = Mockito.mock(Message.class); + GenericMessage genericMessage = Mockito.mock(GenericMessage.class); + GenericMessageWarp genericMessageWarp = Mockito.mock(GenericMessageWarp.class); + + + @Test + void getInstance() { + assertNotNull(instance); + } + + @Test + void getMsg(){ + assertNotNull(instance.getMsg(messageChannel,message)); + assertTrue(instance.getMsg(messageChannel,message).length == 0); + assertInstanceOf(byte[].class,instance.getMsg(messageChannel,message)); + } + + @Test + void warpMC(){ + assertNotNull(instance.warpMC(messageChannel)); + assertInstanceOf(MessageChannel.class,instance.warpMC(messageChannel)); + assertNull(instance.warpMC(new Object())); + } + + @Test + void warpMessage(){ + assertNotNull(instance.warpMessage(message)); + assertNull(instance.warpMessage(null)); + assertInstanceOf(GenericMessageWarp.class,instance.warpMessage(genericMessageWarp)); + when(genericMessage.getPayload()).thenReturn(new Object()); + assertInstanceOf(GenericMessage.class,instance.warpMessage(genericMessage)); + } + + @Test + void getHeader(){ + assertNull(instance.getHeader(messageChannel,message,null)); + assertNull(instance.getHeader(messageChannel,message,"")); + Map temp = new HashMap<>(); + temp.put("mock-key","mock-value"); + when(genericMessage.getHeaders()).thenReturn(new MessageHeaders(temp)); + assertSame("mock-value",instance.getHeader(messageChannel,genericMessage,"mock-key")); + } + + @Test + void removeHeader(){ + assertFalse(instance.removeHeader(messageChannel,null,"mock-key")); + assertFalse(instance.removeHeader(messageChannel,genericMessageWarp,"")); + Map temp = new HashMap<>(); + temp.put("mock-key","mock-value"); + when(genericMessageWarp.getHeaders()).thenReturn(new MessageHeaders(temp)); + assertTrue(instance.removeHeader(messageChannel,genericMessageWarp,"mock-key")); + + assertFalse(instance.removeHeader(messageChannel,message,"mock-key")); + assertFalse(instance.removeHeader(messageChannel,genericMessage,"mock-key")); + } + + @Test + void addHeader(){ + assertFalse(instance.addHeader(messageChannel,null,"mock-key","mock-value")); + assertFalse(instance.addHeader(messageChannel,message,"mock-key","mock-value")); + assertFalse(instance.addHeader(messageChannel,genericMessage,"mock-key","mock-value")); + + when(genericMessageWarp.getHeaders()).thenReturn(new MessageHeaders(new HashMap<>())); + assertTrue(instance.addHeader(messageChannel,genericMessageWarp,"mock-key","mock-value")); + } + + @Test + void resetMsg(){ + assertNull(instance.resetMsg(null)); + + Map temp = new HashMap<>(); + temp.put("mock-key","mock-value"); + when(genericMessageWarp.getHeaders()).thenReturn(new MessageHeaders(temp)); + + assertNotNull(instance.resetMsg(genericMessageWarp)); + assertTrue(instance.resetMsg(genericMessageWarp).getHeaders().size() == 3); + assertSame("mock-value",instance.resetMsg(genericMessageWarp).getHeaders().get("mock-key")); + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQGenericInstrumentationV3Test.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQGenericInstrumentationV3Test.java new file mode 100644 index 000000000..62e30ea2c --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQGenericInstrumentationV3Test.java @@ -0,0 +1,65 @@ +package io.arex.inst.mqtt.inst.inst; + + +import io.arex.agent.bootstrap.internal.Pair; +import io.arex.inst.mqtt.MQTTAdapterHelper; +import io.arex.inst.mqtt.inst.MQGenericInstrumentationV3; +import net.bytebuddy.description.type.TypeDescription; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.core.AbstractMessageSendingTemplate; +import org.springframework.messaging.core.GenericMessagingTemplate; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; + +public class MQGenericInstrumentationV3Test { + MQGenericInstrumentationV3 inst = new MQGenericInstrumentationV3(); + + + @BeforeAll + static void setUp() { + Mockito.mockStatic(MQTTAdapterHelper.class); + } + + @AfterAll + static void tearDown() { + Mockito.clearAllCaches(); + } + + + @Test + void typeMatcher() { + assertFalse(inst.typeMatcher().matches(TypeDescription.ForLoadedType.of(AbstractMessageSendingTemplate.class))); + assertTrue(inst.typeMatcher().matches(TypeDescription.ForLoadedType.of(GenericMessagingTemplate.class))); + } + + @Test + void methodAdvices() { + assertEquals(1, inst.methodAdvices().size()); + } + + + @Test + void ServiceAdvice_onEnter() { + Mockito.when(MQTTAdapterHelper.onServiceEnter(any(), any(), any())).thenReturn(null); + assertDoesNotThrow(() -> MQGenericInstrumentationV3.ArrivedAdvice.onEnter(null, null,null)); + + MessageChannel messageChannel = Mockito.mock(MessageChannel.class); + Message message = Mockito.mock(Message.class); + Mockito.when(MQTTAdapterHelper.onServiceEnter(any(), any(), any())).thenReturn(Pair.of(messageChannel, message)); + assertDoesNotThrow(() -> MQGenericInstrumentationV3.ArrivedAdvice.onEnter(null, null,null)); + } + + @Test + void ServiceAdvice_onExit() { + assertDoesNotThrow(() -> MQGenericInstrumentationV3.ArrivedAdvice.onExit(null, null,null)); + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQTTAdapterModuleInstrumentationTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQTTAdapterModuleInstrumentationTest.java new file mode 100644 index 000000000..76ecfcbe3 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/inst/MQTTAdapterModuleInstrumentationTest.java @@ -0,0 +1,30 @@ +package io.arex.inst.mqtt.inst.inst; + +import io.arex.inst.mqtt.inst.MQTTAdapterModuleInstrumentation; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author : MentosL + * @date : 2023/5/21 15:19 + */ +public class MQTTAdapterModuleInstrumentationTest { + MQTTAdapterModuleInstrumentation module = new MQTTAdapterModuleInstrumentation(); + + + @BeforeEach + void setUp() { + } + + @AfterEach + void tearDown() { + } + + @Test + void instrumentationTypes() { + assertEquals(1, module.instrumentationTypes().size()); + } +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/GenericMessageWarpTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/GenericMessageWarpTest.java new file mode 100644 index 000000000..760c7e7aa --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/GenericMessageWarpTest.java @@ -0,0 +1,65 @@ +package io.arex.inst.mqtt.inst.warp; + +import io.arex.inst.mqtt.warp.GenericMessageWarp; +import org.junit.jupiter.api.Test; +import org.springframework.messaging.MessageHeaders; + +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author : MentosL + * @date : 2023/5/21 16:16 + */ +public class GenericMessageWarpTest { + + + @Test + void GenericMessageWarp1(){ + assertNotNull(new GenericMessageWarp(new Object())); + assertThrows(IllegalArgumentException.class,()-> new GenericMessageWarp(null)); + } + + @Test + void GenericMessageWarp2(){ + assertNotNull(new GenericMessageWarp(new Object(),new HashMap())); + assertThrows(IllegalArgumentException.class,()-> new GenericMessageWarp(null,new HashMap())); + } + + + @Test + void GenericMessageWarp3(){ + assertNotNull(new GenericMessageWarp(new Object(),new MessageHeaders(new HashMap<>()))); + assertThrows(IllegalArgumentException.class,()-> new GenericMessageWarp(null,new MessageHeaders(new HashMap<>()))); + assertThrows(IllegalArgumentException.class,()-> new GenericMessageWarp(new Object(),null)); + } + + @Test + void removeHeader(){ + GenericMessageWarp genericMessageWarp = new GenericMessageWarp(new Object()); + genericMessageWarp.removeHeader("id"); + assertEquals(1, genericMessageWarp.getMessageHeaderWarp().size()); + assertEquals(2, genericMessageWarp.getHeaders().size()); + } + + + @Test + void put(){ + GenericMessageWarp genericMessageWarp = new GenericMessageWarp(new Object()); + genericMessageWarp.put("mock-key","mock-value"); + assertEquals(3, genericMessageWarp.getMessageHeaderWarp().size()); + assertEquals(2, genericMessageWarp.getHeaders().size()); + } + + + @Test + void get(){ + GenericMessageWarp genericMessageWarp = new GenericMessageWarp(new Object()); + genericMessageWarp.put("mock-key","mock-value"); + assertEquals(3, genericMessageWarp.getMessageHeaderWarp().size()); + assertEquals(2, genericMessageWarp.getHeaders().size()); + assertSame("mock-value",genericMessageWarp.get("mock-key")); + } + +} diff --git a/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/MessageHeaderWarpTest.java b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/MessageHeaderWarpTest.java new file mode 100644 index 000000000..d0915b651 --- /dev/null +++ b/arex-instrumentation/mq/arex-integration-mqtt/src/test/java/io/arex/inst/mqtt/inst/warp/MessageHeaderWarpTest.java @@ -0,0 +1,54 @@ +package io.arex.inst.mqtt.inst.warp; + +import io.arex.inst.mqtt.warp.MessageHeaderWarp; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.messaging.MessageHeaders; + +import java.util.HashMap; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +/** + * @author : MentosL + * @date : 2023/5/21 15:39 + */ +public class MessageHeaderWarpTest { + + MessageHeaders messageHeaders = Mockito.mock(MessageHeaders.class); + + @Test + void MessageHeaderWarp1(){ + assertNotNull(new MessageHeaderWarp(messageHeaders)); + MessageHeaders messageHeadersTemp = new MessageHeaders(new HashMap<>()); + assertTrue(new MessageHeaderWarp(messageHeadersTemp).size() == 2); + } + + @Test + void MessageHeaderWarp2(){ + assertNotNull(new MessageHeaderWarp(new HashMap<>())); + assertTrue(new MessageHeaderWarp(new HashMap<>()).size() == 2); + } + + @Test + void MessageHeaderWarp3(){ + assertNotNull(new MessageHeaderWarp(new HashMap<>(), UUID.randomUUID(),System.currentTimeMillis())); + assertTrue(new MessageHeaderWarp(new HashMap<>(), UUID.randomUUID(),System.currentTimeMillis()).size() == 2); + } + + @Test + void put(){ + assertNotNull(new MessageHeaderWarp(new HashMap<>()).put("mock-key","mock-value")); + } + + @Test + void remove(){ + assertDoesNotThrow(() -> new MessageHeaderWarp(new HashMap<>()).remove("mock-key")); + MessageHeaderWarp messageHeaderWarp = new MessageHeaderWarp(new HashMap<>(), UUID.randomUUID(), System.currentTimeMillis()); + assertSame(2,messageHeaderWarp.size()); + messageHeaderWarp.remove("id"); + assertSame(1,messageHeaderWarp.size()); + } + + +} diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter b/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter deleted file mode 100644 index 24bad6272..000000000 --- a/arex-instrumentation/servlet/arex-httpservlet/src/main/resources/META-INF/services/io.arex.inst.httpservlet.convert.HttpMessageConverter +++ /dev/null @@ -1,2 +0,0 @@ -io.arex.inst.httpservlet.convert.impl.ApplicationJsonBodyConverter -io.arex.inst.httpservlet.convert.impl.ApplicationXmlBodyConverter \ No newline at end of file diff --git a/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters b/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters deleted file mode 100644 index 4937f54ab..000000000 --- a/arex-instrumentation/servlet/arex-httpservlet/src/test/resources/META-INF.services/io.arex.inst.httpservlet.convert.BodyConverters +++ /dev/null @@ -1,2 +0,0 @@ -io.arex.inst.httpservlet.convert.impl.ApplicationJsonBodyConvert -io.arex.inst.httpservlet.convert.impl.ApplicationXmlBodyConvert \ No newline at end of file