From fff8e47d07b27c610129992fd67d3db0057cd027 Mon Sep 17 00:00:00 2001 From: swager253 Date: Thu, 2 Jan 2025 15:19:45 +0900 Subject: [PATCH 01/25] =?UTF-8?q?(#21)=20tracer=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traffichunter/agent/engine/AgentExecutionEngine.java | 2 +- .../configuration/ConfigurableContextInitializer.java | 6 +++--- .../{agent => }/trace/opentelemetry/TraceExporter.java | 2 +- .../{agent => }/trace/opentelemetry/TraceManager.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename java-agent/src/main/java/ygo/traffichunter/{agent => }/trace/opentelemetry/TraceExporter.java (98%) rename java-agent/src/main/java/ygo/traffichunter/{agent => }/trace/opentelemetry/TraceManager.java (98%) diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java index c6f7d11b..6ab51553 100644 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java +++ b/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java @@ -40,7 +40,7 @@ import ygo.traffichunter.agent.engine.sender.manager.MetricSendSessionManager; import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.agent.trace.opentelemetry.TraceManager; +import ygo.traffichunter.trace.opentelemetry.TraceManager; /** *

diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java index 0ab2b193..636a4f5b 100644 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java +++ b/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java @@ -57,9 +57,9 @@ import ygo.traffichunter.agent.engine.instrument.annotation.AnnotationPath; import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.agent.trace.opentelemetry.TraceExporter; -import ygo.traffichunter.agent.trace.opentelemetry.TraceManager; -import ygo.traffichunter.agent.trace.opentelemetry.TraceManager.SpanScope; +import ygo.traffichunter.trace.opentelemetry.TraceExporter; +import ygo.traffichunter.trace.opentelemetry.TraceManager; +import ygo.traffichunter.trace.opentelemetry.TraceManager.SpanScope; import ygo.traffichunter.util.UUIDGenerator; /** diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceExporter.java b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java similarity index 98% rename from java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceExporter.java rename to java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java index 99dd3d8e..8caf2ed5 100644 --- a/java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceExporter.java +++ b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package ygo.traffichunter.agent.trace.opentelemetry; +package ygo.traffichunter.trace.opentelemetry; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceManager.java b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java similarity index 98% rename from java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceManager.java rename to java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java index f46b9d41..f5acdb32 100644 --- a/java-agent/src/main/java/ygo/traffichunter/agent/trace/opentelemetry/TraceManager.java +++ b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package ygo.traffichunter.agent.trace.opentelemetry; +package ygo.traffichunter.trace.opentelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; From 27f470250b8468cacb47f77e41029a555d5575a0 Mon Sep 17 00:00:00 2001 From: swager253 Date: Sat, 4 Jan 2025 17:16:28 +0900 Subject: [PATCH 02/25] =?UTF-8?q?(#22)=20javaagent=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 16 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-bootstrap/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-bootstrap/gradlew.bat | 89 +++++++ .../javaagent/bootstrap/BootstrapMain.java | 58 +++++ .../bootstrap/banner/AsciiBanner.java | 65 +++++ .../engine/AgentExecutionEngine.java | 232 +++++++++++++++++ .../TrafficHunterAgentShutdownHook.java | 79 ++++++ .../collect/AbstractMBeanMetricCollector.java | 94 +++++++ .../engine/collect/MetricCollectSupport.java | 132 ++++++++++ .../engine/collect/MetricCollector.java | 54 ++++ .../dbcp/hikari/HikariCPMetricCollector.java | 58 +++++ .../systeminfo/cpu/CpuMetricCollector.java | 72 ++++++ .../gc/GarbageCollectionMetricCollector.java | 74 ++++++ .../memory/MemoryMetricCollector.java | 97 ++++++++ .../runtime/RuntimeMetricCollector.java | 74 ++++++ .../thread/ThreadMetricCollector.java | 72 ++++++ .../web/tomcat/TomcatMetricCollector.java | 86 +++++++ .../context/AgentExecutableContext.java | 68 +++++ .../ConfigurableContextInitializer.java | 189 ++++++++++++++ .../TrafficHunterAgentExecutableContext.java | 191 ++++++++++++++ .../engine/env/ConfigurableEnvironment.java | 52 ++++ .../bootstrap/engine/env/Environment.java | 54 ++++ .../env/yaml/YamlConfigurableEnvironment.java | 120 +++++++++ .../env/yaml/bind/RelaxedBindingUtils.java | 59 +++++ .../env/yaml/root/RootYamlProperty.java | 50 ++++ .../env/yaml/root/agent/AgentSubProperty.java | 102 ++++++++ .../root/agent/retry/RetrySubProperty.java | 68 +++++ .../retry/backoff/BackOffSubProperty.java | 58 +++++ .../instrument/annotation/AnnotationPath.java | 57 +++++ .../instrument/bootstrap/BootState.java | 46 ++++ .../classloading/THAgentClassLoader.java | 59 +++++ .../instrument/locator/AgentLocator.java | 59 +++++ .../bootstrap/engine/jvm/JVMSelector.java | 86 +++++++ .../bootstrap/engine/lifecycle/LifeCycle.java | 88 +++++++ .../engine/property/TrafficHunterAgent.java | 122 +++++++++ .../property/TrafficHunterAgentProperty.java | 86 +++++++ .../FaultTolerantTrafficHunterAgent.java | 89 +++++++ .../bootstrap/engine/sender/MetricSender.java | 41 +++ .../manager/MetricSendSessionManager.java | 183 ++++++++++++++ .../websocket/AgentSystemMetricSender.java | 77 ++++++ .../AgentTransactionMetricSender.java | 87 +++++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-commons/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-commons/gradlew.bat | 89 +++++++ .../commons/dto/dbcp/HikariDbcpInfo.java | 37 +++ .../commons/dto/metadata/AgentMetadata.java | 86 +++++++ .../commons/dto/metadata/MetadataWrapper.java | 35 +++ .../commons/dto/systeminfo/SystemInfo.java | 49 ++++ .../dto/systeminfo/cpu/CpuStatusInfo.java | 31 +++ .../gc/GarbageCollectionStatusInfo.java | 34 +++ .../gc/collections/GarbageCollectionTime.java | 40 +++ .../systeminfo/memory/MemoryStatusInfo.java | 33 +++ .../systeminfo/runtime/RuntimeStatusInfo.java | 36 +++ .../systeminfo/thread/ThreadStatusInfo.java | 35 +++ .../dto/web/tomcat/TomcatWebServerInfo.java | 37 +++ .../web/tomcat/request/TomcatRequestInfo.java | 37 +++ .../tomcat/thread/TomcatThreadPoolInfo.java | 35 +++ .../javaagent/commons/status/AgentStatus.java | 34 +++ .../javaagent/commons/util/AgentUtil.java | 60 +++++ .../javaagent/commons/util/FileUtils.java | 48 ++++ .../javaagent/commons/util/UUIDGenerator.java | 82 ++++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-event/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-event/gradlew.bat | 89 +++++++ .../javaagent/event/AgentEvent.java | 54 ++++ .../AgentContextStateEventHandler.java | 43 ++++ .../listener/AgentStateEventListener.java | 36 +++ .../event/object/AgentStateEvent.java | 66 +++++ .../event/store/AgentStateEventStore.java | 74 ++++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-retry/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-retry/gradlew.bat | 89 +++++++ .../javaagent/retry/RetryHelper.java | 169 +++++++++++++ .../retry/backoff/BackOffPolicy.java | 65 +++++ .../policy/ExponentialBackOffPolicy.java | 39 +++ .../backoff/policy/FixedBackOffPolicy.java | 39 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-trace/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-trace/gradlew.bat | 89 +++++++ .../javaagent/trace/dto/TraceInfo.java | 85 +++++++ .../trace/exporter/TraceExporter.java | 78 ++++++ .../javaagent/trace/manager/TraceManager.java | 72 ++++++ .../javaagent/trace/queue/TraceQueue.java | 63 +++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + java-agent/java-agent-websocket/gradlew | 234 ++++++++++++++++++ java-agent/java-agent-websocket/gradlew.bat | 89 +++++++ .../websocket/MetricWebSocketClient.java | 163 ++++++++++++ .../SerializationByteArrayConverter.java | 133 ++++++++++ .../plugin/sdk/constant/PluginConstant.java | 10 + .../PluginInstrumentation.java | 9 + .../type/TypeInstrumentation.java | 16 ++ .../plugin/sdk/loader/PluginLoader.java | 8 + .../sdk/loader/TrafficHunterPluginLoader.java | 23 ++ .../SpringWebMvcPluginInstrumentation.java | 36 +++ settings.gradle | 12 +- 102 files changed, 7354 insertions(+), 16 deletions(-) create mode 100644 java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-bootstrap/gradlew create mode 100644 java-agent/java-agent-bootstrap/gradlew.bat create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java create mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java create mode 100644 java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-commons/gradlew create mode 100644 java-agent/java-agent-commons/gradlew.bat create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java create mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java create mode 100644 java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-event/gradlew create mode 100644 java-agent/java-agent-event/gradlew.bat create mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java create mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java create mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java create mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java create mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java create mode 100644 java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-retry/gradlew create mode 100644 java-agent/java-agent-retry/gradlew.bat create mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java create mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java create mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java create mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java create mode 100644 java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-trace/gradlew create mode 100644 java-agent/java-agent-trace/gradlew.bat create mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java create mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java create mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java create mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java create mode 100644 java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/java-agent-websocket/gradlew create mode 100644 java-agent/java-agent-websocket/gradlew.bat create mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java create mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java create mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java create mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java create mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java create mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java create mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java create mode 100644 java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java diff --git a/build.gradle b/build.gradle index 464d9360..f1e69102 100644 --- a/build.gradle +++ b/build.gradle @@ -1,24 +1,10 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.4' - id 'io.spring.dependency-management' version '1.1.6' } -group = 'ygo' +group = 'org.traffichunter' version = '0.0.1-SNAPSHOT' -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() } diff --git a/java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-bootstrap/gradlew.bat b/java-agent/java-agent-bootstrap/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-bootstrap/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java new file mode 100644 index 00000000..cd761694 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.lang.instrument.Instrumentation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.AgentExecutionEngine; +import org.traffichunter.javaagent.bootstrap.engine.env.Environment; +import org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap.BootState; + +/** + * main. + * @author yungwang-o + * @version 1.0.0 + */ +public class BootstrapMain { + + private static final Logger log = LoggerFactory.getLogger(BootstrapMain.class); + + private static final BootState STATE = new BootState(); + + public static void premain(String agentArgs, Instrumentation inst) { + + final boolean success = STATE.start(); + if(!success) { + log.error("traffic-hunter-agent-bootstrap already started. skipping agent loading."); + return; + } + + AgentExecutionEngine.run(Environment.SYSTEM_PROFILE.systemProfile(), inst); + } + + public static void agentmain(String agentArgs, Instrumentation inst) { + premain(agentArgs, inst); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java new file mode 100644 index 00000000..95ad86b1 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.banner; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; +import java.util.stream.Collectors; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; + +/** + * banner print. + * @author yungwang-o + * @version 1.0.0 + */ +public class AsciiBanner { + + private static final String BANNER_NAME = "agent-banner.txt"; + + public void print(final AgentMetadata metadata) { + try (final InputStream in = AsciiBanner.class.getClassLoader().getResourceAsStream(BANNER_NAME)) { + + BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(in))); + + String banner = reader.lines() + .map(line -> line + .replace("${version}", metadata.agentVersion()) + .replace("${java.version}", System.getProperty("java.version")) + .replace("${java.specification}", System.getProperty("java.specification.version")) + .replace("${jdk}", System.getProperty("java.vendor")) + .replace("${time}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))) + .collect(Collectors.joining(System.lineSeparator())); + + System.out.println(banner + "\n"); + + } catch (IOException ignored) { + + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java new file mode 100644 index 00000000..45ed3993 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java @@ -0,0 +1,232 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine; + +import java.lang.instrument.Instrumentation; +import java.time.Duration; +import java.time.Instant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.banner.AsciiBanner; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.context.execute.TrafficHunterAgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.lifecycle.LifeCycle; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.queue.TraceQueue; + +/** + *

+ * The {@code AgentExecutionEngine} class is the core execution engine responsible for + * initializing, configuring, running, and shutting down the TrafficHunter Agent. + * This class encapsulates all the necessary steps to bootstrap the agent, manage its + * lifecycle, and ensure proper cleanup upon application termination. + *

+ *

Purpose:

+ * + * + *

Key Components:

+ * + * + *

Limitations:

+ * + * + * @see TrafficHunterAgentShutdownHook + * @see AgentRunner + * @see ConfigurableEnvironment + * @see Instrumentation + * @author yungwang-o + * @version 1.0.0 + */ +public final class AgentExecutionEngine { + + private static final Logger log = LoggerFactory.getLogger(AgentExecutionEngine.class); + + private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); + + private final AsciiBanner asciiBanner = new AsciiBanner(); + + private final ConfigurableEnvironment environment; + + private final Instrumentation inst; + + private AgentExecutionEngine(final String args, final Instrumentation inst) { + this.inst = inst; + this.environment = new YamlConfigurableEnvironment(args); + } + + /** + * Initializes and executes the TrafficHunter Agent. + *

This method performs the following tasks:

+ * + */ + private void run() { + StartUp startUp = new StartUp(); + Instant startTime = startUp.getStartTime(); + final AgentExecutableContext context = new TrafficHunterAgentExecutableContext(environment, shutdownHook); + if(!shutdownHook.isEnabledShutdownHook()) { + shutdownHook.enableShutdownHook(); + } + ConfigurableContextInitializer configurableContextInitializer = context.init(); + configurableContextInitializer.retransform(inst); + TrafficHunterAgentProperty property = configurableContextInitializer.property(); + AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( + startTime, + AgentStatus.INITIALIZED + ); + context.addAgentStateEventListener(metadata); + asciiBanner.print(metadata); + AgentRunner runner = new AgentRunner(property, context, metadata); + Thread runnerThread = new Thread(runner); + runnerThread.setName("TrafficHunterAgentRunnerThread"); + if(context.isInit()) { + log.info("Agent initialization completed."); + runnerThread.start(); + registryShutdownHook(context, runner); + context.close(); + } + + log.info("Started TrafficHunter Agent in {} second", String.format("%.3f", startUp.getUpTime())); + } + + /** + * Registers shutdown hooks for the agent's cleanup operations. + * + * @param context The execution context of the agent. + * @param runner The agent runner instance responsible for managing execution. + */ + private void registryShutdownHook(final AgentExecutableContext context, final AgentRunner runner) { + shutdownHook.addRuntimeShutdownHook(TraceManager::close); + shutdownHook.addRuntimeShutdownHook(TraceQueue.INSTANCE::removeAll); + shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); + shutdownHook.addRuntimeShutdownHook(runner::close); + } + + public static void run(final String args, final Instrumentation inst) { + new AgentExecutionEngine(System.getProperty(args), inst).run(); + } + + /** + * The {@code StartUp} class extends {@link LifeCycle} to measure the agent's + * startup durations. + * + *

Features:

+ * + */ + private static class StartUp extends LifeCycle { + + public StartUp() { + super(); + } + + @Override + public Instant getStartTime() { + return this.startTime; + } + + @Override + public Instant getEndTime() { + if(endTime == null) { + this.endTime = Instant.now(); + } + return endTime; + } + + @Override + public Double getUpTime() { + if(getStartTime() == null && getEndTime() == null) { + throw new IllegalStateException("No start time or end time specified"); + } + + return Duration.between(getStartTime(), getEndTime()).toMillis() / 1_000.0; + } + } + + /** + * The {@code AgentRunner} class implements {@link Runnable} to execute the core logic + * of the TrafficHunter Agent in a separate thread. It initializes the session manager + * and orchestrates agent operations after the target application is loaded. + * + *

Features:

+ * + */ + private static final class AgentRunner implements Runnable { + + private final MetricSendSessionManager sessionManager; + + public AgentRunner(final TrafficHunterAgentProperty property, + final AgentExecutableContext context, + final AgentMetadata metadata) { + + this.sessionManager = new MetricSendSessionManager(property, context, metadata); + } + + /** + * Executes the agent's core logic. Introduces a delay to ensure that + * the target application is fully loaded before starting. + */ + @Override + public void run() { + try { + log.info("Waiting for Agent Runner..."); + Thread.sleep(8000); + sessionManager.run(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public void close() { + sessionManager.close(); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java new file mode 100644 index 00000000..5bec461a --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@code TrafficHunterAgentShutdownHook} class is responsible for managing and + * registering shutdown hooks to execute specific actions when the Java application + * terminates. This class allows users to add custom {@link Runnable} actions that + * will be executed gracefully during the shutdown process. + * + * @see Runtime#addShutdownHook(Thread) + * @see Runnable + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgentShutdownHook implements Runnable { + + private static final Logger log = LoggerFactory.getLogger(TrafficHunterAgentShutdownHook.class); + + private volatile boolean enabledShutdownHook = false; + + private final Set actions = ConcurrentHashMap.newKeySet(); + + public void enableShutdownHook() { + enabledShutdownHook = true; + } + + public void addRuntimeShutdownHook(final Runnable action) { + actions.add(action); + } + + @Override + public void run() { + log.info("register shutdown hook"); + + if(!enabledShutdownHook) { + log.info("shutdown hook not enabled"); + return; + } + + for(Runnable action : actions) { + Runtime.getRuntime().addShutdownHook(new Thread(action, nameShutdownThread(action.getClass().getName()))); + } + } + + public boolean isEnabledShutdownHook() { + return enabledShutdownHook; + } + + private static String nameShutdownThread(final String threadName) { + return threadName + "-ShutdownHook"; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java new file mode 100644 index 00000000..41c9483f --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java @@ -0,0 +1,94 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect; + +import java.lang.management.ManagementFactory; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * The {@code AbstractMBeanMetricCollector} abstract class provides a base implementation + * for collecting metrics using the JMX {@link MBeanServer}. + * + *

Features:

+ * + * + * @see MetricCollector + * @see MBeanServer + * + * @author yungwang-o + * @version 1.0.0 + */ +public abstract class AbstractMBeanMetricCollector implements MetricCollector { + + protected final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + protected Set findObjectName(final String objectName) { + try { + return mBeanServer.queryNames(new ObjectName(objectName), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + } + + protected A getAttribute(final ObjectName name, final String attribute, final Class type) { + try { + + return type.cast(mBeanServer.getAttribute(name, attribute)); + } catch (Exception e) { + throw new MetricCollectionException("Failed to get attribute: " + attribute, e); + } + } + + public static class MetricCollectionException extends RuntimeException { + + public MetricCollectionException() { + super(); + } + + public MetricCollectionException(final String message) { + super(message); + } + + public MetricCollectionException(final String message, final Throwable cause) { + super(message, cause); + } + + public MetricCollectionException(final Throwable cause) { + super(cause); + } + + protected MetricCollectionException(final String message, final Throwable cause, + final boolean enableSuppression, + final boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java new file mode 100644 index 00000000..aa9b3198 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java @@ -0,0 +1,132 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect; + +import java.io.IOException; +import java.time.Instant; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.collect.dbcp.hikari.HikariCPMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.cpu.CpuMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.gc.GarbageCollectionMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory.MemoryMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.runtime.RuntimeMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.thread.ThreadMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.web.tomcat.TomcatMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.jvm.JVMSelector; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.commons.dto.systeminfo.SystemInfo; + +/** + * The {@code MetricCollectSupport} class provides functionality for collecting system metrics + * from the local JVM or a target JVM via JMX. + * + *

Features:

+ *
    + *
  • Integrates multiple metric collectors for memory, CPU, threads, garbage collection, + * runtime, Tomcat, and HikariCP metrics.
  • + *
  • Supports collecting metrics from the local JVM or a remote JVM identified by a JMX path.
  • + *
  • Combines collected metrics into a unified {@link SystemInfo} object.
  • + *
+ * + * @see SystemInfo + * @see MemoryMetricCollector + * @see CpuMetricCollector + * @see ThreadMetricCollector + * @see GarbageCollectionMetricCollector + * @see RuntimeMetricCollector + * @see TomcatMetricCollector + * @see HikariCPMetricCollector + * + * @author yungwang-o + * @version 1.0.0 + */ + +public class MetricCollectSupport { + + private static final Logger log = LoggerFactory.getLogger(MetricCollectSupport.class); + + private final MemoryMetricCollector collectorMemory; + + private final CpuMetricCollector collectorCpu; + + private final ThreadMetricCollector collectorThread; + + private final GarbageCollectionMetricCollector collectorGC; + + private final RuntimeMetricCollector collectorRuntime; + + private final TomcatMetricCollector collectorTomcat; + + private final HikariCPMetricCollector collectorHikari; + + public MetricCollectSupport(final TrafficHunterAgentProperty property) { + this.collectorMemory = new MemoryMetricCollector(); + this.collectorCpu = new CpuMetricCollector(); + this.collectorThread = new ThreadMetricCollector(); + this.collectorGC = new GarbageCollectionMetricCollector(); + this.collectorRuntime = new RuntimeMetricCollector(); + this.collectorTomcat = new TomcatMetricCollector(property); + this.collectorHikari = new HikariCPMetricCollector(); + } + + public SystemInfo collect(final String targetJVMPath) { + + try (final JMXConnector jmxConnector = JMXConnectorFactory.connect(JVMSelector.getVMXServiceUrl(targetJVMPath))) { + + final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection(); + + return new SystemInfo( + Instant.now(), + collectorMemory.collect(mbsc), + collectorThread.collect(mbsc), + collectorCpu.collect(mbsc), + collectorGC.collect(mbsc), + collectorRuntime.collect(mbsc), + collectorTomcat.collect(mbsc), + collectorHikari.collect(mbsc) + ); + + } catch (IOException e) { + log.error("Failed to start local management agent = {}", e.getMessage()); + throw new RuntimeException(e); + } + } + + public SystemInfo collect() { + return new SystemInfo( + Instant.now(), + collectorMemory.collect(), + collectorThread.collect(), + collectorCpu.collect(), + collectorGC.collect(), + collectorRuntime.collect(), + collectorTomcat.collect(), + collectorHikari.collect() + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java new file mode 100644 index 00000000..deb7bc79 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect; + +import java.util.List; +import javax.management.MBeanServerConnection; + +/** + * The {@code MetricCollector} interface defines a contract for collecting metrics. + * Implementations can collect metrics from various sources, such as the local JVM + * or an MBeanServer. + * + *

Features:

+ *
    + *
  • Provides methods for collecting a single metric or all available metrics.
  • + *
  • Includes default implementations for optional operations.
  • + *
+ * + * @param The type of metric being collected. + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface MetricCollector { + + default T collect(MBeanServerConnection mbsc) { + return null; + } + + T collect(); + + default List collectAll() {return null;} +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java new file mode 100644 index 00000000..8b183383 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.dbcp.hikari; + +import javax.management.ObjectName; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.commons.dto.dbcp.HikariDbcpInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class HikariCPMetricCollector extends AbstractMBeanMetricCollector { + + @Override + public HikariDbcpInfo collect() { + try { + + //The default name for a Hikari connection pool is "HikariPool-1" + ObjectName hikariInfo = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)"); + + int activeConnections = getAttribute(hikariInfo, "ActiveConnections", Integer.class); + int idleConnections = getAttribute(hikariInfo, "IdleConnections", Integer.class); + int totalConnections = getAttribute(hikariInfo, "TotalConnections", Integer.class); + int threadsAwaitingConnections = getAttribute(hikariInfo, "ThreadsAwaitingConnection", Integer.class); + + return new HikariDbcpInfo( + activeConnections, + idleConnections, + totalConnections, + threadsAwaitingConnections + ); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java new file mode 100644 index 00000000..9b1e47a0 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.cpu; + +import com.sun.management.OperatingSystemMXBean; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.commons.dto.systeminfo.cpu.CpuStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class CpuMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(CpuStatusInfo.class.getName()); + + @Override + public CpuStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final OperatingSystemMXBean osMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, + OperatingSystemMXBean.class + ); + + return new CpuStatusInfo( + osMXBean.getCpuLoad(), + osMXBean.getProcessCpuLoad(), + osMXBean.getAvailableProcessors() + ); + } catch (IOException e) { + log.warning("Failed to get cpu metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public CpuStatusInfo collect() { + final OperatingSystemMXBean osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + + return new CpuStatusInfo( + osMXBean.getCpuLoad(), + osMXBean.getProcessCpuLoad(), + osMXBean.getAvailableProcessors() + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java new file mode 100644 index 00000000..956fe472 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.gc; + +import java.io.IOException; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory.MemoryMetricCollector; +import org.traffichunter.javaagent.commons.dto.systeminfo.gc.GarbageCollectionStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections.GarbageCollectionTime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class GarbageCollectionMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); + + @Override + public GarbageCollectionStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final List mxBeans = ManagementFactory.getPlatformMXBeans(mbsc, + GarbageCollectorMXBean.class); + + final List garbageCollectionTimes = mxBeans + .stream() + .map(GarbageCollectionTime::new) + .toList(); + + return new GarbageCollectionStatusInfo(garbageCollectionTimes); + } catch (IOException e) { + log.warning("Failed to get GC metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public GarbageCollectionStatusInfo collect() { + final List mxBeans = ManagementFactory.getGarbageCollectorMXBeans(); + + final List garbageCollectionTimes = mxBeans + .stream() + .map(GarbageCollectionTime::new) + .toList(); + + return new GarbageCollectionStatusInfo(garbageCollectionTimes); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java new file mode 100644 index 00000000..03f77a37 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo.MemoryUsage; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class MemoryMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); + + @Override + public MemoryStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.MEMORY_MXBEAN_NAME, + MemoryMXBean.class + ); + + final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); + final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); + + return new MemoryStatusInfo( + new MemoryUsage( + heapMemoryUsage.getInit(), + heapMemoryUsage.getUsed(), + heapMemoryUsage.getCommitted(), + heapMemoryUsage.getMax() + ), + new MemoryUsage( + nonHeapMemoryUsage.getInit(), + nonHeapMemoryUsage.getUsed(), + nonHeapMemoryUsage.getCommitted(), + nonHeapMemoryUsage.getMax() + ) + ); + } catch (IOException e) { + log.warning("Failed to get heap memory metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public MemoryStatusInfo collect() { + final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + + final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); + final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); + + return new MemoryStatusInfo( + new MemoryUsage( + heapMemoryUsage.getInit(), + heapMemoryUsage.getUsed(), + heapMemoryUsage.getCommitted(), + heapMemoryUsage.getMax() + ), + new MemoryUsage( + nonHeapMemoryUsage.getInit(), + nonHeapMemoryUsage.getUsed(), + nonHeapMemoryUsage.getCommitted(), + nonHeapMemoryUsage.getMax() + ) + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java new file mode 100644 index 00000000..ef9570d8 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.runtime; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.commons.dto.systeminfo.runtime.RuntimeStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RuntimeMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(RuntimeStatusInfo.class.getName()); + + @Override + public RuntimeStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.RUNTIME_MXBEAN_NAME, + RuntimeMXBean.class + ); + + return new RuntimeStatusInfo( + runtimeMXBean.getStartTime(), + runtimeMXBean.getUptime(), + runtimeMXBean.getVmName(), + runtimeMXBean.getVmVersion() + ); + } catch (IOException e) { + log.warning("Failed to get runtime metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public RuntimeStatusInfo collect() { + final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + + return new RuntimeStatusInfo( + runtimeMXBean.getStartTime(), + runtimeMXBean.getUptime(), + runtimeMXBean.getVmName(), + runtimeMXBean.getVmVersion() + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java new file mode 100644 index 00000000..439390ac --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.thread; + +import com.sun.management.ThreadMXBean; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.commons.dto.systeminfo.thread.ThreadStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class ThreadMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(ThreadMetricCollector.class.getName()); + + @Override + public ThreadStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.THREAD_MXBEAN_NAME, + ThreadMXBean.class + ); + + return new ThreadStatusInfo( + threadMXBean.getThreadCount(), + threadMXBean.getPeakThreadCount(), + threadMXBean.getTotalStartedThreadCount() + ); + } catch (IOException e) { + log.warning("Failed to get thread metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public ThreadStatusInfo collect() { + final ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); + + return new ThreadStatusInfo( + threadMXBean.getThreadCount(), + threadMXBean.getPeakThreadCount(), + threadMXBean.getTotalStartedThreadCount() + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java new file mode 100644 index 00000000..00b6664c --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.collect.web.tomcat; + +import javax.management.ObjectName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.commons.dto.web.tomcat.TomcatWebServerInfo; +import org.traffichunter.javaagent.commons.dto.web.tomcat.request.TomcatRequestInfo; +import org.traffichunter.javaagent.commons.dto.web.tomcat.thread.TomcatThreadPoolInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class TomcatMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = LoggerFactory.getLogger(TomcatMetricCollector.class.getName()); + + private final TrafficHunterAgentProperty property; + + public TomcatMetricCollector(final TrafficHunterAgentProperty property) { + this.property = property; + } + + @Override + public TomcatWebServerInfo collect() { + + try { + ObjectName threadPoolMbean = new ObjectName("Tomcat:type=ThreadPool,name=" + getPort()); + ObjectName requestMBean = new ObjectName("Tomcat:type=GlobalRequestProcessor,name=" + getPort()); + + int maxThreads = getAttribute(threadPoolMbean, "maxThreads", Integer.class); + int currentThreads = getAttribute(threadPoolMbean, "currentThreadCount", Integer.class); + int currentThreadsBusy = getAttribute(threadPoolMbean, "currentThreadsBusy", Integer.class); + + int requestCount = getAttribute(requestMBean, "requestCount", Integer.class); + long bytesReceived = getAttribute(requestMBean, "bytesReceived", Long.class); + long bytesSent = getAttribute(requestMBean, "bytesSent", Long.class); + long processingTime = getAttribute(requestMBean, "processingTime", Long.class); + int errorCount = getAttribute(requestMBean, "errorCount", Integer.class); + + return new TomcatWebServerInfo( + new TomcatThreadPoolInfo(maxThreads, currentThreads, currentThreadsBusy), + new TomcatRequestInfo(requestCount, bytesReceived, bytesSent, processingTime, errorCount) + ); + } catch (Exception e) { + log.error("Failed to collect metrics = {}", e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * target uri -> localhost:8080 + *
+ * translation -> localhost:8080 -> http-nio-8080 + * @return port + */ + private String getPort() { + final String port = property.targetUri().split(":")[1]; + return String.format("\"http-nio-%s\"", port); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java new file mode 100644 index 00000000..99d4b0c1 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context; + +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.context.AgentContextStateEventHandler; + +/** + * The {@code AgentExecutableContext} interface defines the contract for managing + * the lifecycle of an agent's execution context. It includes methods for initializing + * the context, managing its state, and handling lifecycle events. + * + *

Features:

+ *
    + *
  • Extends {@link AgentContextStateEventHandler} for state event listener management.
  • + *
  • Provides methods to initialize, close, and query the context's status.
  • + *
  • Supports dynamic updates to the agent's execution status.
  • + *
+ * + * @see AgentContextStateEventHandler + * @see ConfigurableContextInitializer + * @see ConfigurableEnvironment + * @see AgentStatus + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface AgentExecutableContext extends AgentContextStateEventHandler { + + ConfigurableContextInitializer init(); + + void close(); + + ConfigurableEnvironment getEnvironment(); + + AgentStatus getStatus(); + + boolean isInit(); + + boolean isRunning(); + + boolean isStopped(); + + void setStatus(AgentStatus status); +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java new file mode 100644 index 00000000..342c0180 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java @@ -0,0 +1,189 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context.configuration; + +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import java.io.InputStream; +import java.lang.instrument.Instrumentation; +import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder.Default; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.Enter; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.asm.Advice.Origin; +import net.bytebuddy.asm.Advice.Thrown; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; +import net.bytebuddy.matcher.ElementMatchers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.Environment; +import org.traffichunter.javaagent.bootstrap.engine.instrument.annotation.AnnotationPath; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.commons.util.UUIDGenerator; +import org.traffichunter.javaagent.trace.exporter.TraceExporter; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +/** + * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, + * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. + * + *

Features:

+ *
    + *
  • Loads properties using a configurable environment.
  • + *
  • Sets up agent metadata based on the current environment and runtime status.
  • + *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • + *
+ * + * @see ConfigurableEnvironment + * @see TrafficHunterAgentProperty + * @see ByteBuddy + * @see AgentMetadata + * + * @author yungwang-o + * @version 1.0.0 + */ +public class ConfigurableContextInitializer { + + private static final Logger log = LoggerFactory.getLogger(ConfigurableContextInitializer.class); + + private final ConfigurableEnvironment env; + + public ConfigurableContextInitializer(final ConfigurableEnvironment env) { + this.env = env; + } + + public TrafficHunterAgentProperty property() { + return env.load(); + } + + public TrafficHunterAgentProperty property(final InputStream is) { + return env.load(is); + } + + public void retransform(final Instrumentation inst) { + new Default() + .ignore(ignoreMatchPackage()) + .type(getSpringComponentMatcher()) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> + builder.visit(Advice.to(TransactionAdvise.class).on(isMethod())) + ).installOn(inst); + } + + public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { + + final String agentName = property().name(); + + return new AgentMetadata( + UUIDGenerator.generate(agentName), + Environment.VERSION.version(), + agentName, + startTime, + new AtomicReference<>(status) + ); + } + + private Junction ignoreMatchPackage() { + return ElementMatchers.nameStartsWith("java.") + .or(ElementMatchers.nameStartsWith("sun.")) + .or(ElementMatchers.nameStartsWith("jdk.")); + } + + private ElementMatcher getSpringComponentMatcher() { + return isAnnotatedWith(named(AnnotationPath.SERVICE.getPath())) + .or(isAnnotatedWith(named(AnnotationPath.REST_CONTROLLER.getPath()))) + .or(isAnnotatedWith(named(AnnotationPath.CONTROLLER.getPath()))) + .or(isAnnotatedWith(named(AnnotationPath.REPOSITORY.getPath()))); + } + + /** + *

+ * Intercepts method execution to create a span for tracing. + * Handles span lifecycle, recording exceptions, and closing the scope. + *

+ * + *

Note: Ensure the class has a public access modifier to avoid + * visibility issues when used with external components or frameworks like ByteBuddy. + * Using a private or package-private access modifier may result in runtime errors + * due to restricted access.

+ * + * @see TraceManager + */ + public static class TransactionAdvise { + + public static final Tracer tracer = TraceManager.configure(new TraceExporter()); + + @OnMethodEnter + public static SpanScope enter(@Origin final String method) { + + Span currentSpan = Span.current(); + + Span span = tracer.spanBuilder(method) + .setParent(Context.current().with(currentSpan)) + .setAttribute("method.name", method) + .setStartTimestamp(Instant.now()) + .startSpan(); + + Scope scope = span.makeCurrent(); + + return new SpanScope(span, scope); + } + + @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + + if (Objects.nonNull(throwable)) { + + String exception = throwable.getClass().getName() + + " " + + "(" + + throwable.getMessage() + + ")"; + + spanScope.span().recordException(throwable); + spanScope.span().setStatus(StatusCode.ERROR, exception); + } + + spanScope.span().end(Instant.now()); + spanScope.scope().close(); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java new file mode 100644 index 00000000..5d523642 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java @@ -0,0 +1,191 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context.execute; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.traffichunter.javaagent.bootstrap.engine.TrafficHunterAgentShutdownHook; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; +import org.traffichunter.javaagent.event.object.AgentStateEvent; +import org.traffichunter.javaagent.event.store.AgentStateEventStore; + +/** + * The {@code TrafficHunterAgentExecutableContext} class represents the execution context + * for the TrafficHunter Agent. It manages the agent's state, event listeners, environment + * configuration, and shutdown operations. + * + *

Purpose:

+ *
    + *
  • Maintains the current state of the agent using an atomic {@link AgentStatus}.
  • + *
  • Registers and manages {@link AgentStateEventListener} instances for state change notifications.
  • + *
  • Integrates a shutdown mechanism using {@link TrafficHunterAgentShutdownHook}.
  • + *
+ * + *

Key Features:

+ *
    + *
  • {@code init()} - Initializes the agent with the provided environment settings.
  • + *
  • {@code addAgentStateEventListener()} - Adds a listener to observe state changes.
  • + *
  • {@code removeAllAgentStateEventListeners()} - Removes all registered listeners.
  • + *
  • {@code close()} - Safely shuts down the agent using a dedicated shutdown thread.
  • + *
  • {@code setStatus()} - Atomically updates the agent's state and notifies listeners of the change.
  • + *
+ * + *

Thread Safety:

+ *
    + *
  • The {@code status} is managed using {@link AtomicReference}, ensuring thread-safe updates.
  • + *
  • Shutdown operations are protected by a {@link ReentrantLock} to prevent concurrent execution.
  • + *
  • {@link AtomicBoolean} is used to ensure the shutdown logic executes only once.
  • + *
+ * + *

Limitations:

+ *
    + *
  • State listeners are invoked sequentially; long-running listeners may delay others.
  • + *
  • Shutdown operations rely on an additional thread, which may cause delays during JVM termination.
  • + *
+ * + * @see AgentExecutableContext + * @see AgentStatus + * @see TrafficHunterAgentShutdownHook + * @see ConfigurableContextInitializer + * @see AgentStateEventListener + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgentExecutableContext extends AgentStateEventStore implements AgentExecutableContext { + + private final ConfigurableEnvironment environment; + + private final AtomicReference status = new AtomicReference<>(AgentStatus.INITIALIZED); + + private final TrafficHunterAgentShutdownHook shutdownHook; + + private final ReentrantLock shutdownLock = new ReentrantLock(); + + private final AtomicBoolean isShutdown = new AtomicBoolean(false); + + public TrafficHunterAgentExecutableContext(final ConfigurableEnvironment environment, + final TrafficHunterAgentShutdownHook shutdownHook) { + this.environment = environment; + this.shutdownHook = shutdownHook; + } + + @Override + public void addAgentStateEventListener(final AgentStateEventListener listener) { + super.addAgentStateEventListener(listener); + } + + @Override + public void removeAgentStateEventListener(final AgentStateEventListener listener) { + super.removeAgentStateEventListener(listener); + } + + @Override + public void removeAllAgentStateEventListeners() { + super.removeAll(); + } + + @Override + public ConfigurableContextInitializer init() { + return new ConfigurableContextInitializer(environment); + } + + /** + * Safely shuts down the agent by executing the registered shutdown hooks. + *

Ensures that:

+ *
    + *
  • The shutdown hook is executed only once.
  • + *
  • Concurrent shutdown attempts are prevented using a {@link ReentrantLock}.
  • + *
+ * If the shutdown hook is not enabled, this method does nothing. + */ + @Override + public void close() { + + if(isStopped()) { + return; + } + + if(this.shutdownHook.isEnabledShutdownHook() && this.isShutdown.compareAndSet(false, true)) { + Thread shutdownHookThread; + shutdownLock.lock(); + try { + shutdownHookThread = new Thread(this.shutdownHook, "TrafficHunterAgentShutdownHook"); + shutdownHookThread.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + shutdownLock.unlock(); + } + } + } + + @Override + public ConfigurableEnvironment getEnvironment() { + return environment; + } + + @Override + public AgentStatus getStatus() { + return status.get(); + } + + @Override + public boolean isInit() { + return status.get() == AgentStatus.INITIALIZED; + } + + @Override + public boolean isRunning() { + return status.get() == AgentStatus.RUNNING; + } + + @Override + public boolean isStopped() { + return status.get() == AgentStatus.EXIT; + } + + @Override + public void setStatus(final AgentStatus newStatus) { + AgentStatus agentStatus; + + do { + agentStatus = status.get(); + } while (!status.compareAndSet(agentStatus, newStatus)); + + AgentStateEvent event = new AgentStateEvent(this, agentStatus, newStatus); + + notifyAgentStateChange(event); + } + + private void notifyAgentStateChange(final AgentStateEvent event) { + for(AgentStateEventListener listener : super.getListeners()) { + listener.onEvent(event); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java new file mode 100644 index 00000000..0908db02 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env; + +import java.io.InputStream; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; + +/** + * The {@code ConfigurableEnvironment} interface defines the contract for loading + * agent configuration properties. It supports loading configuration from + * default or provided input sources. + * + *

Features:

+ *
    + *
  • Loads configuration properties into a {@link TrafficHunterAgentProperty} instance.
  • + *
  • Supports default and custom input streams for configuration sources.
  • + *
+ * + * @see TrafficHunterAgentProperty + * @see YamlConfigurableEnvironment + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface ConfigurableEnvironment { + + TrafficHunterAgentProperty load(); + + TrafficHunterAgentProperty load(InputStream is); +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java new file mode 100644 index 00000000..7c923b76 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public enum Environment { + + DEFAULT_PATH("/env/agent-env.yml"), + VERSION("1.0.0"), + SYSTEM_PROFILE("traffichunter.config"), + ; + + private final String env; + + Environment(final String env) { + this.env = env; + } + + public String path() { + return env; + } + + public String version() { + return env; + } + + public String systemProfile() { + return env; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java new file mode 100644 index 00000000..a19d6860 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml; + +import java.io.InputStream; +import java.util.concurrent.TimeUnit; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind.RelaxedBindingUtils; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.RootYamlProperty; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.commons.util.FileUtils; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; +import org.traffichunter.javaagent.retry.backoff.policy.ExponentialBackOffPolicy; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +/** + * The {@code YamlConfigurableEnvironment} class implements {@link ConfigurableEnvironment} + * to load configuration properties from a YAML file or input stream. + * + *

Features:

+ *
    + *
  • Loads configuration properties from a YAML file or input stream.
  • + *
  • Parses YAML data into a {@link TrafficHunterAgentProperty} instance using + * custom relaxed binding rules.
  • + *
  • Supports a default configuration file or custom file paths.
  • + *
+ * + * @see ConfigurableEnvironment + * @see TrafficHunterAgentProperty + * + * @author yungwang-o + * @version 1.0.0 + */ +public class YamlConfigurableEnvironment implements ConfigurableEnvironment { + + private static final String DEFAULT_CONFIG_FILE = "agent-env.yml"; + + private final Yaml yaml; + + private final String path; + + public YamlConfigurableEnvironment(final String path) { + final Constructor constructor = new Constructor(RootYamlProperty.class, new LoaderOptions()); + + constructor.setPropertyUtils(new RelaxedBindingUtils()); + + this.yaml = new Yaml(constructor); + this.path = path; + } + + @Override + public TrafficHunterAgentProperty load() { + final InputStream is = FileUtils.getFile(path); + + final RootYamlProperty root = yaml.load(is); + + return YamlParser.parse(root); + } + + @Override + public TrafficHunterAgentProperty load(final InputStream is) { + + final RootYamlProperty root = yaml.load(is); + + return YamlParser.parse(root); + } + + static class YamlParser { + + private static TrafficHunterAgentProperty parse(final RootYamlProperty root){ + + return TrafficHunterAgent.connect(root.getAgent().getServerUri()) + .name(root.getAgent().getName()) + .targetUri(root.getAgent().getTargetUri()) + .locationJar(root.getAgent().getJar()) + .scheduleInterval(root.getAgent().getInterval()) + .scheduleTimeUnit(TimeUnit.SECONDS) + .faultTolerant() + .retry(root.getAgent().getRetry().getMaxAttempt()) + .backOffPolicy(backOffPolicy(root.getAgent().getRetry().getBackoff())) + .complete(); + } + + private static BackOffPolicy backOffPolicy(BackOffSubProperty backOffSubProperty) { + if(backOffSubProperty == null) { + return ExponentialBackOffPolicy.DEFAULT; + } + + return new ExponentialBackOffPolicy( + backOffSubProperty.getIntervalMillis(), + backOffSubProperty.getMultiplier() + ); + } + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java new file mode 100644 index 00000000..cfbfe221 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.introspector.Property; +import org.yaml.snakeyaml.introspector.PropertyUtils; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RelaxedBindingUtils extends PropertyUtils { + + private static final Logger log = LoggerFactory.getLogger(RelaxedBindingUtils.class); + + @Override + public Property getProperty(final Class type, final String name) { + return super.getProperty(type, kebabToCamel(name)); + } + + private String kebabToCamel(final String kebab) { + final StringBuilder sb = new StringBuilder(); + + String[] split = kebab.split("-"); + + for(int i = 0; i < split.length; i++) { + if(i == 0) { + sb.append(split[i]); + continue; + } + sb.append(split[i].replaceFirst("^[a-z]", String.valueOf(split[i].charAt(0)).toUpperCase())); + } + + return sb.toString(); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java new file mode 100644 index 00000000..6acec047 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.AgentSubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RootYamlProperty { + + private AgentSubProperty agent; + + public RootYamlProperty() { + } + + public RootYamlProperty(final AgentSubProperty agent) { + this.agent = agent; + } + + public AgentSubProperty getAgent() { + return agent; + } + + public void setAgent(final AgentSubProperty agent) { + this.agent = agent; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java new file mode 100644 index 00000000..4e4702bb --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java @@ -0,0 +1,102 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.RetrySubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentSubProperty { + + private String name; + private String jar; + private String serverUri; + private String targetUri; + private int interval; + private RetrySubProperty retry; + + public AgentSubProperty() { + } + + public AgentSubProperty(final String name, final String jar, final String serverUri, final String targetUri, + final int interval, + final RetrySubProperty retry) { + this.name = name; + this.jar = jar; + this.serverUri = serverUri; + this.targetUri = targetUri; + this.interval = interval; + this.retry = retry; + } + + public String getServerUri() { + return serverUri; + } + + public void setServerUri(final String serverUri) { + this.serverUri = serverUri; + } + + public String getTargetUri() { + return targetUri; + } + + public void setTargetUri(final String targetUri) { + this.targetUri = targetUri; + } + + public int getInterval() { + return interval; + } + + public void setInterval(final int interval) { + this.interval = interval; + } + + public RetrySubProperty getRetry() { + return retry; + } + + public void setRetry(final RetrySubProperty retry) { + this.retry = retry; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getJar() { + return jar; + } + + public void setJar(final String jar) { + this.jar = jar; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java new file mode 100644 index 00000000..19cef295 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RetrySubProperty { + + private int maxAttempt; + private BackOffSubProperty backoff; + + public RetrySubProperty() { + } + + public RetrySubProperty(final int maxAttempt, final BackOffSubProperty backoff) { + this.maxAttempt = maxAttempt; + this.backoff = backoff; + } + + public int getMaxAttempt() { + return maxAttempt; + } + + public void setMaxAttempt(final int maxAttempt) { + this.maxAttempt = maxAttempt; + } + + public BackOffSubProperty getBackoff() { + return backoff; + } + + public void setBackoff(final BackOffSubProperty backoff) { + this.backoff = backoff; + } + + @Override + public String toString() { + return "RetrySubProperty{" + + "maxAttempt=" + maxAttempt + + ", backoff=" + backoff + + '}'; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java new file mode 100644 index 00000000..2bdb26d6 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class BackOffSubProperty { + + private long intervalMillis; + private int multiplier; + + public BackOffSubProperty() { + } + + public BackOffSubProperty(final long intervalMillis, final int multiplier) { + this.intervalMillis = intervalMillis; + this.multiplier = multiplier; + } + + public long getIntervalMillis() { + return intervalMillis; + } + + public void setIntervalMillis(final long intervalMillis) { + this.intervalMillis = intervalMillis; + } + + public int getMultiplier() { + return multiplier; + } + + public void setMultiplier(final int multiplier) { + this.multiplier = multiplier; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java new file mode 100644 index 00000000..1765afed --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.annotation; + +/** + * The {@code AnnotationPath} enum defines commonly used annotation paths + * for Spring-based applications, which are used for bytecode manipulation + * with ByteBuddy. + * + * @author yungwang-o + * @version 1.0.0 + */ +public enum AnnotationPath { + + TRANSACTIONAL("org.springframework.transaction.annotation.Transactional"), + SERVICE("org.springframework.stereotype.Service"), + REPOSITORY("org.springframework.stereotype.Repository"), + REST_CONTROLLER("org.springframework.web.bind.annotation.RestController"), + CONTROLLER("org.springframework.stereotype.Controller"), + ; + + private final String path; + + AnnotationPath(final String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + public static boolean filter(final String path) { + return path.equals("join") || path.equals("wait") || path.equals("notify") || path.equals("notifyAll") || + path.equals("hashcode") || path.equals("equals") || path.equals("toString"); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java new file mode 100644 index 00000000..bb93bd03 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class BootState { + + private static final Boolean STATE_NONE = false; + private static final Boolean STATE_STARTED = true; + + private final AtomicBoolean state = new AtomicBoolean(STATE_NONE); + + boolean getState() { + return state.get(); + } + + public boolean start() { + return state.compareAndSet(STATE_NONE, STATE_STARTED); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java new file mode 100644 index 00000000..b1d51e3f --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.classloading; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class THAgentClassLoader extends ClassLoader { + + public THAgentClassLoader(final ClassLoader parent) { + super(parent); + } + + @Override + protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + Class clazz = findLoadedClass(name); + + if(clazz == null) { + try { + clazz = findClass(name); + } catch (ClassNotFoundException ignored) { + } + + if(clazz == null) { + clazz = getParent().loadClass(name); + } + } + if(resolve) { + resolveClass(clazz); + } + return clazz; + } + } + + +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java new file mode 100644 index 00000000..452dbef3 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.locator; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import org.traffichunter.javaagent.bootstrap.BootstrapMain; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentLocator { + + public static File getAgentJarFile() throws URISyntaxException { + + ProtectionDomain protectionDomain = BootstrapMain.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + if(codeSource == null) { + throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain)); + } + + URL location = codeSource.getLocation(); + if(location == null) { + throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource)); + } + + final File agentJar = new File(location.toURI()); + if(agentJar.getName().endsWith(".jar")) { + throw new IllegalStateException("Agent is not a jar file: " + agentJar); + } + + return agentJar.getAbsoluteFile(); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java new file mode 100644 index 00000000..c6f8522b --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.jvm; + +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; +import java.io.IOException; +import java.util.Objects; +import java.util.logging.Logger; +import javax.management.remote.JMXServiceURL; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class JVMSelector { + + private static final Logger log = Logger.getLogger(JVMSelector.class.getName()); + + public static JMXServiceURL getVMXServiceUrl(final String targetJVMPath) { + + try { + final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); + + final VirtualMachine vm = VirtualMachine.attach(vmDescriptor.id().trim()); + + final String jmxUrl = vm.startLocalManagementAgent(); + + return new JMXServiceURL(jmxUrl); + } catch (AttachNotSupportedException | IOException e) { + log.warning("Not found jvm service url : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + public static VirtualMachine getVM(final String targetJVMPath) { + + try { + final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); + + return VirtualMachine.attach(vmDescriptor.id().trim()); + + } catch (AttachNotSupportedException | IOException e) { + log.warning("Not found jvm service url : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + private static VirtualMachineDescriptor getVirtualMachineDescriptor(final String targetJVMPath) { + + return VirtualMachine.list().stream() + .filter(vm -> Objects.equals(vm.displayName(), targetJVMPath)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No virtual machine found")); + } + + public static String displayName(final int selectNumber) { + return VirtualMachine.list().get(selectNumber - 1).displayName(); + } + + public static String JvmId(final int selectNumber) { + return VirtualMachine.list().get(selectNumber - 1).id(); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java new file mode 100644 index 00000000..7fd7ea42 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.lifecycle; + +import java.time.Duration; +import java.time.Instant; + +/** + * The {@code LifeCycle} abstract class defines a common structure for managing + * the lifecycle of an object, including its start time, end time, and uptime. + * + *

Features:

+ *
    + *
  • Automatically initializes the start time when an instance is created.
  • + *
  • Provides abstract methods for retrieving the start time, end time, and uptime.
  • + *
  • Allows subclasses to implement specific behaviors for managing lifecycle information.
  • + *
+ * + *

Usage:

+ *
{@code
+ * public class TaskLifeCycle extends LifeCycle {
+ *
+ *     @Override
+ *     public Instant getStartTime() {
+ *         return startTime;
+ *     }
+ *
+ *     @Override
+ *     public Instant getEndTime() {
+ *         if (endTime == null) {
+ *             endTime = Instant.now();
+ *         }
+ *         return endTime;
+ *     }
+ *
+ *     @Override
+ *     public Duration getUpTime() {
+ *         return Duration.between(getStartTime(), getEndTime());
+ *     }
+ * }
+ *
+ * TaskLifeCycle task = new TaskLifeCycle();
+ * System.out.println("Uptime: " + task.getUpTime().toSeconds() + " seconds");
+ * }
+ * + * @see Instant + * @see Duration + * + * @author yungwang-o + * @version 1.0.0 +*/ +public abstract class LifeCycle { + + protected final Instant startTime; + + protected Instant endTime; + + protected LifeCycle() { + this.startTime = Instant.now(); + } + + public abstract Instant getStartTime(); + + public abstract Instant getEndTime(); + + public abstract Double getUpTime(); +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java new file mode 100644 index 00000000..3834e172 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java @@ -0,0 +1,122 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import org.traffichunter.javaagent.bootstrap.engine.property.child.FaultTolerantTrafficHunterAgent; + +/** + *

+ * The {@code TrafficHunterAgent} class is responsible for holding and managing configuration + * details required to set up and execute traffic monitoring or management tasks. + * This class provides a fluent API for constructing and customizing the configuration + * properties, such as scheduling intervals, target URIs, and resource locations. + *

+ * + *

Example Usage:

+ *
{@code
+ * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
+ *     .name("MyAgent")
+ *     .targetUri("localhost:8888")
+ *     .locationJar("/path/to/agent.jar")
+ *     .scheduleInterval(10)
+ *     .scheduleTimeUnit(TimeUnit.MINUTES)
+ *     .complete();
+ * }
+ * + *

Extensibility:

+ *
    + *
  • The class can be extended with custom behavior, as seen in {@link FaultTolerantTrafficHunterAgent}.
  • + *
+ * + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgent { + + private static final Logger log = Logger.getLogger(TrafficHunterAgent.class.getName()); + + protected int scheduleInterval; + + protected String targetUri; + + protected String jar; + + protected final String uri; + + protected TimeUnit timeUnit; + + protected String name; + + protected TrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { + this.name = trafficHunterAgent.name; + this.scheduleInterval = trafficHunterAgent.scheduleInterval; + this.targetUri = trafficHunterAgent.targetUri; + this.uri = trafficHunterAgent.uri; + this.timeUnit = trafficHunterAgent.timeUnit; + this.jar = trafficHunterAgent.jar; + } + + protected TrafficHunterAgent(final String uri) { + this.uri = uri; + } + + public static TrafficHunterAgent connect(final String serverUrl) { + return new TrafficHunterAgent(serverUrl); + } + + public FaultTolerantTrafficHunterAgent faultTolerant() { + return new FaultTolerantTrafficHunterAgent(this); + } + + public TrafficHunterAgent name(final String name) { + this.name = name; + return this; + } + + public TrafficHunterAgent targetUri(final String targetUri) { + this.targetUri = targetUri; + return this; + } + + public TrafficHunterAgent locationJar(final String jar) { + this.jar = jar; + return this; + } + + public TrafficHunterAgent scheduleInterval(final int scheduleInterval) { + this.scheduleInterval = scheduleInterval; + return this; + } + + public TrafficHunterAgent scheduleTimeUnit(final TimeUnit timeUnit) { + this.timeUnit = timeUnit; + return this; + } + + public TrafficHunterAgentProperty complete() { + return new TrafficHunterAgentProperty(name, targetUri, jar, scheduleInterval, uri, timeUnit); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java new file mode 100644 index 00000000..130460ae --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property; + +import java.util.concurrent.TimeUnit; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TrafficHunterAgentProperty( + String name, + String targetUri, + String jar, + int scheduleInterval, + String serverUri, + TimeUnit timeUnit, + int maxAttempt, + BackOffPolicy backOffPolicy +) { + + public TrafficHunterAgentProperty(final String name, + final String targetUri, + final String jar, + final int scheduleInterval, + final String serverUri, + final TimeUnit timeUnit, + final int maxAttempt, + final BackOffPolicy backOffPolicy) { + + this.name = name; + this.targetUri = targetUri; + this.jar = jar; + this.scheduleInterval = scheduleInterval; + this.serverUri = serverUri; + this.timeUnit = timeUnit; + this.maxAttempt = maxAttempt; + this.backOffPolicy = backOffPolicy; + } + + public TrafficHunterAgentProperty(final String name, + final String targetUri, + final String jar, + final int scheduleInterval, + final String serverUri, + final TimeUnit timeUnit) { + + this(name, targetUri, jar, scheduleInterval, serverUri, timeUnit, 0, null); + } + + @Override + public String toString() { + return "TrafficHunterAgentProperty{" + + "name='" + name + '\'' + + ", targetUri='" + targetUri + '\'' + + ", jar=" + jar + + ", scheduleInterval=" + scheduleInterval + + ", serverUri=" + serverUri + + ", timeUnit=" + timeUnit + + ", maxAttempt=" + maxAttempt + + ", backOffPolicy=" + backOffPolicy.toString() + + '}'; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java new file mode 100644 index 00000000..d2c8afc8 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property.child; + +import java.util.logging.Logger; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * The {@code FaultTolerantTrafficHunterAgent} class extends the functionality of + * {@link TrafficHunterAgent} by adding fault-tolerant capabilities, such as retry logic + * and back-off policies. This class is designed to handle scenarios where failures + * may occur and ensures that the agent can recover gracefully. + * + *

Example Usage:

+ *
{@code
+ * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
+ *     .name("ResilientAgent")
+ *     .targetUri("localhost:8888")
+ *     .locationJar("/path/to/agent.jar")
+ *     .scheduleInterval(10)
+ *     .scheduleTimeUnit(TimeUnit.MINUTES)
+ *     .faultTolerant()
+ *     .retry(5) // Set maximum retry attempts to 5
+ *     .backOffPolicy(new ExponentialBackOffPolicy()) // Use exponential back-off policy
+ *     .complete();
+ * }
+ * + * @see TrafficHunterAgent + * @see BackOffPolicy + * @author yungwang-o + * @version 1.0.0 + */ +public class FaultTolerantTrafficHunterAgent extends TrafficHunterAgent { + + private static final Logger log = Logger.getLogger(FaultTolerantTrafficHunterAgent.class.getName()); + private BackOffPolicy backOffPolicy; + private int maxAttempt; + + public FaultTolerantTrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { + super(trafficHunterAgent); + } + + public FaultTolerantTrafficHunterAgent retry(final int maxAttempt) { + this.maxAttempt = maxAttempt; + return this; + } + + public FaultTolerantTrafficHunterAgent backOffPolicy(final BackOffPolicy backOffPolicy) { + this.backOffPolicy = backOffPolicy; + return this; + } + + @Override + public TrafficHunterAgentProperty complete() { + return new TrafficHunterAgentProperty( + this.name, + this.targetUri, + this.jar, + this.scheduleInterval, + this.uri, + this.timeUnit, + maxAttempt, + backOffPolicy + ); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java new file mode 100644 index 00000000..8e822c49 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender; + +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; + +/** + * The {@code MetricSender} interface defines the contract for sending metrics. + * Implementations of this interface are responsible for serializing and sending + * specific types of metrics using a given communication client. + * + * @see AgentTransactionMetricSender + * @see AgentSystemMetricSender + */ +public interface MetricSender { + + void toSend(AgentMetadata metadata); +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java new file mode 100644 index 00000000..328e3128 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java @@ -0,0 +1,183 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.manager; + +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.commons.util.AgentUtil; +import org.traffichunter.javaagent.retry.RetryHelper; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; + +/** + * The {@code MetricSendSessionManager} class manages the session for sending + * transaction and system metrics from the TrafficHunter Agent to a server. + * It handles WebSocket connections, retries, scheduling, and lifecycle management + * of the metric-sending operations. + * + *

Purpose:

+ *
    + *
  • Manages WebSocket connections for real-time metric transmission.
  • + *
  • Schedules periodic system metric transmissions using a {@link ScheduledExecutorService}.
  • + *
  • Retries connection attempts in case of failures with a configurable back-off policy.
  • + *
  • Provides lifecycle management for starting and stopping the session.
  • + *
+ * + *

Key Features:

+ *
    + *
  • {@code run()} - Starts the metric sending session, ensuring retries and scheduling are properly configured.
  • + *
  • {@code close()} - Safely shuts down the session, releasing all resources.
  • + *
  • Integrates with {@link Retry} to handle WebSocket reconnections in case of failures.
  • + *
+ * + *

Thread Management:

+ *
    + *
  • Uses a {@link ScheduledExecutorService} for scheduling periodic system metrics.
  • + *
  • Uses a {@link ExecutorService} for running transaction metric transmissions.
  • + *
  • Both executors are safely shut down during the {@code close()} method.
  • + *
+ * + *

Limitations:

+ *
    + *
  • If the agent's context status is already {@code RUNNING}, the session cannot be restarted without stopping it first.
  • + *
  • Retries only handle specific exceptions, as configured in the {@link RetryHelper}.
  • + *
+ * + * @see Retry + * @see ScheduledExecutorService + * @see MetricWebSocketClient + * @see TrafficHunterAgentProperty + * @see AgentExecutableContext + * @see AgentMetadata + * @see RetryHelper + * @author yungwang-o + * @version 1.0.0 + */ +public class MetricSendSessionManager { + + private static final Logger log = LoggerFactory.getLogger(MetricSendSessionManager.class); + + private final TrafficHunterAgentProperty property; + + private final AgentTransactionMetricSender transactionMetricSender; + + private final AgentSystemMetricSender systemMetricSender; + + private final ScheduledExecutorService schedule; + + private final ExecutorService executor; + + private final AgentExecutableContext context; + + private final AgentMetadata metadata; + + private final MetricWebSocketClient client; + + public MetricSendSessionManager(final TrafficHunterAgentProperty property, + final AgentExecutableContext context, + final AgentMetadata metadata) { + + this.client = new MetricWebSocketClient(AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), metadata); + this.client.connect(); + this.metadata = metadata; + this.context = context; + this.property = property; + this.transactionMetricSender = new AgentTransactionMetricSender(client); + this.systemMetricSender = new AgentSystemMetricSender(client, property); + this.schedule = Executors.newSingleThreadScheduledExecutor(getThreadFactory("TransactionSystemInfoMetricSender")); + this.executor = Executors.newVirtualThreadPerTaskExecutor(); + } + + public void run() { + + if(context.isStopped()) { + log.error("MetricSendSessionManager is stopped"); + return; + } + + if(context.isRunning()) { + log.error("MetricSendSessionManager is already running"); + return; + } + + log.info("start Metric send!!"); + + final RetryHelper retryHelper = RetryHelper.builder() + .backOffPolicy(property.backOffPolicy()) + .isCheck(true) + .retryName("websocket retry") + .maxAttempts(property.maxAttempt()) + .retryPredicate(throwable -> throwable instanceof IllegalStateException) + .build(); + + RetryConfig retryConfig = retryHelper.configureRetry(); + + Retry retry = Retry.of(retryHelper.getRetryName(), retryConfig); + + retry.getEventPublisher() + .onRetry(event -> { + client.reconnect(); + log.info("{} retry {} attempts...", event.getName(), event.getNumberOfRetryAttempts()); + }); + + context.setStatus(AgentStatus.RUNNING); + + executor.execute(Retry.decorateRunnable(retry, () -> transactionMetricSender.toSend(metadata))); + + schedule.scheduleWithFixedDelay(Retry.decorateRunnable(retry, () -> systemMetricSender.toSend(metadata)), + 0, + property.scheduleInterval(), + property.timeUnit() + ); + } + + public void close() { + log.info("closing MetricSendSessionManager..."); + context.setStatus(AgentStatus.EXIT); + client.close(); + executor.shutdown(); + schedule.shutdown(); + } + + private ThreadFactory getThreadFactory(final String threadName) { + return r -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName(threadName); + + return thread; + }; + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java new file mode 100644 index 00000000..5ae34f94 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.collect.MetricCollectSupport; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; +import org.traffichunter.javaagent.commons.dto.systeminfo.SystemInfo; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; + +/** + * The {@code AgentSystemMetricSender} class is responsible for sending system metrics + * to the server via a WebSocket connection. + * + *

Features:

+ *
    + *
  • Collects system metric data from the local environment.
  • + *
  • Wraps the system data with metadata and sends it in a compressed format.
  • + *
  • Uses {@link MetricCollectSupport} for metric collection.
  • + *
+ * + * @see MetricSender + * @see MetricWebSocketClient + * @see MetricCollectSupport + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentSystemMetricSender implements MetricSender { + + private static final Logger log = LoggerFactory.getLogger(AgentSystemMetricSender.class); + + private final MetricWebSocketClient client; + + private final MetricCollectSupport metricCollectSupport; + + public AgentSystemMetricSender(final MetricWebSocketClient client, + final TrafficHunterAgentProperty property) { + this.client = client; + this.metricCollectSupport = new MetricCollectSupport(property); + } + + @Override + public void toSend(final AgentMetadata metadata) { + final SystemInfo systemInfo = metricCollectSupport.collect(); + + final MetadataWrapper wrapper = MetadataWrapper.create(metadata, systemInfo); + + client.compressToSend(wrapper, MetricType.SYSTEM_METRIC); + } +} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java new file mode 100644 index 00000000..74ef2345 --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; +import org.traffichunter.javaagent.trace.dto.TraceInfo; +import org.traffichunter.javaagent.trace.queue.TraceQueue; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; + +/** + *

+ * The {@code AgentTransactionMetricSender} class is responsible for sending transaction metrics + * to the server via a WebSocket connection. + *

+ * + *

Features:

+ *
    + *
  • Continuously retrieves transaction data from a synchronized queue.
  • + *
  • Wraps the transaction data with metadata and sends it in a compressed format.
  • + *
  • Relies on {@link TraceQueue} for thread-safe access to transaction data.
  • + *
+ * + * @see MetricSender + * @see MetricWebSocketClient + * @see TraceQueue + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentTransactionMetricSender implements MetricSender { + + public static final Logger log = LoggerFactory.getLogger(AgentTransactionMetricSender.class); + + private final MetricWebSocketClient client; + + public AgentTransactionMetricSender(final MetricWebSocketClient client) { + this.client = client; + } + + @Override + public void toSend(final AgentMetadata metadata) { + + while (!Thread.currentThread().isInterrupted()) { + + try { + + TraceInfo trInfo = TraceQueue.INSTANCE.poll(); + + MetadataWrapper wrapper = MetadataWrapper.create(metadata, trInfo); + + client.compressToSend(wrapper, MetricType.TRANSACTION_METRIC); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (IllegalStateException e) { + log.error("exception while sending transaction metric = {}", e.getMessage()); + throw new RuntimeException(e); + } + } + } +} diff --git a/java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-commons/gradlew.bat b/java-agent/java-agent-commons/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-commons/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java new file mode 100644 index 00000000..92c59078 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.dbcp; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record HikariDbcpInfo( + // Connection Status + int activeConnections, // Number of active connections currently in use + int idleConnections, // Number of idle connections in the pool + int totalConnections, // Total number of connections in the pool + int threadsAwaitingConnection // Number of threads waiting for a connection +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java new file mode 100644 index 00000000..4850d399 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.metadata; + +import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; +import org.traffichunter.javaagent.event.object.AgentStateEvent; + +/** + * The {@code AgentMetadata} record represents metadata for an agent and acts as an + * {@link AgentStateEventListener} to respond to state change events. + * + *

Features:

+ *
    + *
  • Stores immutable metadata fields such as agent ID, version, name, and start time.
  • + *
  • Maintains a mutable {@link AtomicReference} for the agent's current status.
  • + *
  • Implements {@link AgentStateEventListener} to react to state change events.
  • + *
  • Overrides {@code equals} and {@code hashCode} to handle mutable status appropriately.
  • + *
+ * + * @see AgentStateEventListener + * @see AgentStateEvent + * @see AgentStatus + * @see AtomicReference + * + * @author yungwang-o + * @version 1.0.0 + */ +public record AgentMetadata( + String agentId, + String agentVersion, + String agentName, + Instant startTime, + AtomicReference status +) implements AgentStateEventListener { + + @Override + public void onEvent(final AgentStateEvent event) { + this.setStatus(event.getAfterStatus()); + } + + private void setStatus(final AgentStatus status) { + this.status.set(status); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AgentMetadata that = (AgentMetadata) o; + return Objects.equals(agentId, that.agentId) && + Objects.equals(agentVersion, that.agentVersion) && + Objects.equals(agentName, that.agentName) && + Objects.equals(startTime, that.startTime) && + Objects.equals(status.get(), that.status.get()); + } + + @Override + public int hashCode() { + return Objects.hash(agentId, agentVersion, agentName, startTime, status.get()); + } +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java new file mode 100644 index 00000000..0d626faf --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.metadata; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record MetadataWrapper(AgentMetadata metadata, D data) { + + public static MetadataWrapper create(AgentMetadata metadata, D data) { + return new MetadataWrapper<>(metadata, data); + } +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java new file mode 100644 index 00000000..119ed493 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo; + +import java.time.Instant; +import org.traffichunter.javaagent.commons.dto.dbcp.HikariDbcpInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.cpu.CpuStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.gc.GarbageCollectionStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.runtime.RuntimeStatusInfo; +import org.traffichunter.javaagent.commons.dto.systeminfo.thread.ThreadStatusInfo; +import org.traffichunter.javaagent.commons.dto.web.tomcat.TomcatWebServerInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record SystemInfo( + Instant time, + MemoryStatusInfo memoryStatusInfo, + ThreadStatusInfo threadStatusInfo, + CpuStatusInfo cpuStatusInfo, + GarbageCollectionStatusInfo garbageCollectionStatusInfo, + RuntimeStatusInfo runtimeStatusInfo, + TomcatWebServerInfo tomcatWebServerInfo, + HikariDbcpInfo hikariDbcpInfo +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java new file mode 100644 index 00000000..3ea992b9 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.cpu; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record CpuStatusInfo(double systemCpuLoad, double processCpuLoad, long availableProcessors) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java new file mode 100644 index 00000000..06ac48c1 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.gc; + +import java.util.List; +import org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections.GarbageCollectionTime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record GarbageCollectionStatusInfo(List garbageCollectionTimes) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java new file mode 100644 index 00000000..a55ccac0 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections; + +import java.lang.management.GarbageCollectorMXBean; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record GarbageCollectionTime( + long getCollectionCount, + long getCollectionTime +) { + + public GarbageCollectionTime(final GarbageCollectorMXBean mxBean) { + this(mxBean.getCollectionCount(), mxBean.getCollectionTime()); + } +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java new file mode 100644 index 00000000..58b13550 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.memory; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record MemoryStatusInfo(MemoryUsage heapMemoryUsage, MemoryUsage nonHeapMemoryUsage) { + + public record MemoryUsage(long init, long used, long committed, long max) {} +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java new file mode 100644 index 00000000..69dd8be1 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.runtime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record RuntimeStatusInfo( + long getStartTime, + long getUpTime, + String getVmName, + String getVmVersion +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java new file mode 100644 index 00000000..6fc95de0 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.systeminfo.thread; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record ThreadStatusInfo( + int threadCount, + int getPeekThreadCount, + long getTotalStartThreadCount +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java new file mode 100644 index 00000000..bdd53433 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.web.tomcat; + +import org.traffichunter.javaagent.commons.dto.web.tomcat.request.TomcatRequestInfo; +import org.traffichunter.javaagent.commons.dto.web.tomcat.thread.TomcatThreadPoolInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatWebServerInfo( + TomcatThreadPoolInfo tomcatThreadPoolInfo, + TomcatRequestInfo tomcatRequestInfo +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java new file mode 100644 index 00000000..4e434855 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.web.tomcat.request; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatRequestInfo( + long requestCount, + long bytesReceived, + long bytesSent, + long processingTime, + long errorCount +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java new file mode 100644 index 00000000..fae0d023 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.dto.web.tomcat.thread; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatThreadPoolInfo( + int maxThreads, + int currentThreads, + int currentThreadsBusy +) { +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java new file mode 100644 index 00000000..a514ab8f --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.status; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public enum AgentStatus { + INITIALIZED, + RUNNING, + EXIT +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java new file mode 100644 index 00000000..fd781fc2 --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.util; + +import java.net.URI; + +/** + * The {@code AgentUtil} enum provides utility methods for constructing + * WebSocket and HTTP URLs for the TrafficHunter Agent. It also includes + * methods for validating and formatting server addresses. + * + * @author yungwang-o + * @version 1.0.0 +*/ +public enum AgentUtil { + WEBSOCKET_URL("ws://%s/traffic-hunter/tx"), + HTTP_URL("http://%s/traffic-hunter"), + ; + + private static final String pattern = "^(localhost|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):\\d{1,5}$"; + + private final String url; + + AgentUtil(final String url) { + this.url = url; + } + + public static boolean isAddr(final String serverUrl) { + return serverUrl.matches(pattern); + } + + public String getUrl(final String serverUrl) { + return String.format(url, serverUrl); + } + + public URI getUri(final String serverUrl) { + return URI.create(String.format(url, serverUrl)); + } +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java new file mode 100644 index 00000000..60389eef --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.util; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.logging.Logger; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class FileUtils { + + private static final Logger log = Logger.getLogger(FileUtils.class.getName()); + + public static FileInputStream getFile(final String path) { + try { + return new FileInputStream(path); + } catch (FileNotFoundException e) { + log.severe("Could not open file " + path + " " + e); + throw new RuntimeException(e); + } + } + + +} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java new file mode 100644 index 00000000..f2cbee9d --- /dev/null +++ b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java @@ -0,0 +1,82 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.commons.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.UUID; +import java.util.logging.Logger; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class UUIDGenerator { + + private static final Logger log = Logger.getLogger(UUIDGenerator.class.getName()); + + public static String generate(final String agentName) { + Path path = Paths.get(getPath(agentName)); + + if(!Files.exists(path.getParent())) { + try { + Files.createDirectories(path.getParent()); + } catch (IOException e) { + log.severe("Failed to create directory " + e.getMessage()); + throw new RuntimeException(e); + } + } + + if(Files.exists(path)) { + try { + return Files.readString(path).trim(); + } catch (IOException e) { + log.severe("Failed to read file " + e.getMessage()); + throw new RuntimeException(e); + } + } + + final String uuid = UUID.randomUUID().toString(); + + try { + Files.writeString(path, uuid); + return uuid; + } catch (IOException e) { + log.severe("Failed to write file " + e.getMessage()); + throw new RuntimeException(e); + } + } + + private static String getPath(final String agentName) { + return System.getProperty("user.home") + + "/traffic-hunter" + + "/key" + + "/" + + agentName + + "_" + + "agent_id.txt"; + } +} diff --git a/java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-event/gradlew.bat b/java-agent/java-agent-event/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-event/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java new file mode 100644 index 00000000..3dc955eb --- /dev/null +++ b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.event; + +import java.time.Instant; +import java.util.EventObject; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public abstract class AgentEvent extends EventObject { + + private final long timestamp; + + public AgentEvent(final Object source) { + super(source); + this.timestamp = System.currentTimeMillis(); + } + + public AgentEvent(final Object source, final Instant instant) { + super(source); + this.timestamp = instant.toEpochMilli(); + } + + public final long getTimestamp() { + return this.timestamp; + } + + public Object getSource() { + return super.getSource(); + } +} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java new file mode 100644 index 00000000..9a4d50d7 --- /dev/null +++ b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.event.context; + +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; + +/** + * The {@code AgentContextStateEventHandler} interface defines methods for managing + * {@link AgentStateEventListener} instances. It allows adding, removing, and clearing + * listeners for handling agent state change events. + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface AgentContextStateEventHandler { + + void addAgentStateEventListener(final AgentStateEventListener listener); + + void removeAgentStateEventListener(final AgentStateEventListener listener); + + void removeAllAgentStateEventListeners(); +} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java new file mode 100644 index 00000000..a6a74855 --- /dev/null +++ b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.event.listener; + +import java.util.EventListener; +import org.traffichunter.javaagent.event.object.AgentStateEvent; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public interface AgentStateEventListener extends EventListener { + + void onEvent(AgentStateEvent event); +} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java new file mode 100644 index 00000000..8d97e0fa --- /dev/null +++ b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.event.object; + +import java.time.Instant; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.AgentEvent; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentStateEvent extends AgentEvent { + + private final AgentStatus beforeStatus; + + private final AgentStatus afterStatus; + + public AgentStateEvent(final Object source, + final AgentStatus beforeStatus, + final AgentStatus afterStatus) { + + super(source); + this.beforeStatus = beforeStatus; + this.afterStatus = afterStatus; + } + + public AgentStateEvent(final Object source, + final Instant instant, + final AgentStatus beforeStatus, + final AgentStatus afterStatus) { + + super(source, instant); + this.beforeStatus = beforeStatus; + this.afterStatus = afterStatus; + } + + public AgentStatus getBeforeStatus() { + return this.beforeStatus; + } + + public AgentStatus getAfterStatus() { + return this.afterStatus; + } +} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java new file mode 100644 index 00000000..9ac280b6 --- /dev/null +++ b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.event.store; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; + +/** + * The {@code AgentStateEventStore} class manages a collection of {@link AgentStateEventListener} instances. + * It provides methods to add, remove, and retrieve listeners, enabling event-driven state management + * for the TrafficHunter Agent. + * + *

Purpose:

+ *
    + *
  • Stores event listeners for agent state changes.
  • + *
  • Optimized for frequent reads and infrequent writes using {@link CopyOnWriteArrayList}.
  • + *
+ * + * @see AgentStateEventListener + * @see CopyOnWriteArrayList + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentStateEventStore { + + private final List listeners = new CopyOnWriteArrayList<>(); + + protected void addAgentStateEventListener(final AgentStateEventListener listener) { + listeners.add(listener); + } + + protected void removeAgentStateEventListener(final AgentStateEventListener listener) { + if(listeners.isEmpty()) { + throw new IllegalArgumentException("listener is empty"); + } + + listeners.remove(listener); + } + + public List getListeners() { + return listeners; + } + + protected void removeAll() { + listeners.clear(); + } + + public int listenerSize() { + return listeners.size(); + } +} diff --git a/java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-retry/gradlew.bat b/java-agent/java-agent-retry/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-retry/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java new file mode 100644 index 00000000..e8f96491 --- /dev/null +++ b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java @@ -0,0 +1,169 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry; + +import io.github.resilience4j.core.IntervalFunction; +import io.github.resilience4j.retry.RetryConfig; +import java.util.function.Predicate; +import java.util.logging.Logger; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * The {@code RetryHelper} class simplifies the configuration of retry logic, + * including backoff policies, maximum attempts, and exception handling. + * + *

Features:

+ *
    + *
  • Provides a fluent {@code Builder} for configuring retry parameters.
  • + *
  • Supports customizable backoff policies via {@link BackOffPolicy}.
  • + *
  • Configures exception-based retry logic with a {@link Predicate}.
  • + *
  • Generates a {@link RetryConfig} compatible with retry libraries.
  • + *
+ * + *

Example:

+ *
{@code
+ * RetryHelper retryHelper = RetryHelper.builder()
+ *     .backOffPolicy(new BackOffPolicy(1000, 2))
+ *     .maxAttempts(5)
+ *     .retryPredicate(e -> e instanceof IllegalStateException)
+ *     .retryName("SampleRetry")
+ *     .isCheck(true)
+ *     .build();
+ *
+ * RetryConfig retryConfig = retryHelper.configureRetry();
+ * }
+ * + *

Thread Safety:

+ *
    + *
  • This class is immutable and thread-safe once built.
  • + *
+ * + * @see RetryConfig + * @see BackOffPolicy + * + * @author yungwang-o + * @version 1.0.0 + */ +public class RetryHelper { + + private static final Logger log = Logger.getLogger(RetryHelper.class.getName()); + + private final BackOffPolicy backOffPolicy; + + private final int maxAttempts; + + private final Predicate retryPredicate; + + private final String retryName; + + private final boolean isCheck; + + private RetryHelper(final Builder builder) { + this.backOffPolicy = builder.backOffPolicy; + this.maxAttempts = builder.maxAttempts; + this.retryPredicate = builder.retryPredicate; + this.retryName = builder.retryName; + this.isCheck = builder.isCheck; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private BackOffPolicy backOffPolicy; + + private int maxAttempts; + + private Predicate retryPredicate; + + private String retryName; + + private boolean isCheck; + + public Builder() { + } + + public Builder backOffPolicy(final BackOffPolicy backOffPolicy) { + this.backOffPolicy = backOffPolicy; + return this; + } + + public Builder maxAttempts(final int maxAttempts) { + this.maxAttempts = maxAttempts; + return this; + } + + public Builder retryPredicate(final Predicate retryPredicate) { + this.retryPredicate = retryPredicate; + return this; + } + + public Builder retryName(final String retryName) { + this.retryName = retryName; + return this; + } + + public Builder isCheck(final boolean isCheck) { + this.isCheck = isCheck; + return this; + } + + public RetryHelper build() { + return new RetryHelper(this); + } + } + + public BackOffPolicy getBackOffPolicy() { + return backOffPolicy; + } + + public int getMaxAttempts() { + return maxAttempts; + } + + public Predicate getRetryPredicate() { + return retryPredicate; + } + + public String getRetryName() { + return retryName; + } + + public boolean isCheck() { + return isCheck; + } + + public RetryConfig configureRetry() { + return RetryConfig.custom() + .maxAttempts(maxAttempts) + .retryOnException(retryPredicate) + .failAfterMaxAttempts(isCheck) + .intervalFunction(IntervalFunction.ofExponentialRandomBackoff( + backOffPolicy.getIntervalMillis(), + backOffPolicy.getMultiplier() + )).build(); + } +} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java new file mode 100644 index 00000000..14916566 --- /dev/null +++ b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff; + +/** + * The {@code BackOffPolicy} class serves as the base class for defining backoff strategies + * used in retry mechanisms. It provides common properties such as the interval and multiplier, + * which can be extended for specific backoff behaviors. + * + *

Subclasses:

+ *
    + *
  • {@link ExponentialBackOffPolicy}: Implements exponential backoff strategy.
  • + *
  • {@link FixedBackOffPolicy}: Implements fixed interval backoff strategy.
  • + *
+ * + * @author yungwang-o + * @version 1.0.0 + */ +public class BackOffPolicy { + + private final long intervalMillis; + private final int multiplier; + + public BackOffPolicy(final long intervalMillis, final int multiplier) { + this.intervalMillis = intervalMillis; + this.multiplier = multiplier; + } + + public long getIntervalMillis() { + return intervalMillis; + } + + public int getMultiplier() { + return multiplier; + } + + @Override + public String toString() { + return "BackOffPolicy{" + + "intervalMillis=" + intervalMillis + + ", multiplier=" + multiplier + + '}'; + } +} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java new file mode 100644 index 00000000..586bbb00 --- /dev/null +++ b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff.policy; + +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class ExponentialBackOffPolicy extends BackOffPolicy { + + public static final ExponentialBackOffPolicy DEFAULT = new ExponentialBackOffPolicy(1000, 2); + + public ExponentialBackOffPolicy(final long intervalMillis, final int multiplier) { + super(intervalMillis, multiplier); + } +} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java new file mode 100644 index 00000000..8c3597d4 --- /dev/null +++ b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff.policy; + +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class FixedBackOffPolicy extends BackOffPolicy { + + public static final FixedBackOffPolicy DEFAULT = new FixedBackOffPolicy(1000); + + public FixedBackOffPolicy(final long intervalMillis) { + super(intervalMillis, 1); + } +} diff --git a/java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-trace/gradlew.bat b/java-agent/java-agent-trace/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-trace/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java new file mode 100644 index 00000000..c180b4dd --- /dev/null +++ b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java @@ -0,0 +1,85 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.dto; + +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import java.time.Duration; +import java.time.Instant; +import java.util.Objects; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TraceInfo( + + String name, + + String traceId, + + String parentSpanId, + + String spanId, + + Instant startTime, + + Instant endTime, + + long duration, + + String exception, + + boolean ended +) { + + public static TraceInfo translate(final SpanData spanData) { + + Instant startTime = Instant.EPOCH.plusNanos(spanData.getStartEpochNanos()); + + Instant endTime = Instant.EPOCH.plusNanos(spanData.getEndEpochNanos()); + + Duration between = Duration.between(startTime, endTime); + + return new TraceInfo( + spanData.getName(), + spanData.getTraceId(), + spanData.getParentSpanId(), + spanData.getSpanId(), + startTime, + endTime, + between.toMillis(), + getException(spanData), + spanData.hasEnded() + ); + } + + private static String getException(final SpanData spanData) { + if(Objects.equals(spanData.getStatus().getStatusCode(), StatusCode.ERROR)) { + return spanData.getStatus().getDescription(); + } + + return "success"; + } +} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java new file mode 100644 index 00000000..21e41c86 --- /dev/null +++ b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.exporter; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.util.Collection; +import java.util.logging.Logger; +import org.traffichunter.javaagent.trace.dto.TraceInfo; +import org.traffichunter.javaagent.trace.queue.TraceQueue; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class TraceExporter implements SpanExporter { + + private static final Logger log = Logger.getLogger(TraceExporter.class.getName()); + + private volatile boolean isShutdown = false; + + @Override + public CompletableResultCode export(final Collection spans) { + + if(isShutdown) { + return CompletableResultCode.ofFailure(); + } + + try { + spans.stream() + .map(TraceInfo::translate) + .forEach(TraceQueue.INSTANCE::add); + + return CompletableResultCode.ofSuccess(); + } catch (RuntimeException e) { + return CompletableResultCode.ofFailure(); + } + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + + isShutdown = true; + + try { + return CompletableResultCode.ofSuccess(); + } catch (Exception e) { + return CompletableResultCode.ofFailure(); + } + } +} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java new file mode 100644 index 00000000..3aa95a7b --- /dev/null +++ b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.manager; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.IdGenerator; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.util.Objects; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class TraceManager { + + private static final String INSTRUMENTATION_SCOPE_NAME = "instrument-transaction-scope"; + + private static volatile SdkTracerProvider provider; + + private static volatile OpenTelemetrySdk openTelemetrySdk; + + public static Tracer configure(final SpanExporter exporter) { + + provider = SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .setIdGenerator(IdGenerator.random()) + .setSampler(Sampler.alwaysOn()) + .build(); + + openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(provider) + .build(); + + return openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME); + } + + public static void close() { + if (Objects.nonNull(provider) && Objects.nonNull(openTelemetrySdk)) { + provider.close(); + openTelemetrySdk.close(); + } + } + + public record SpanScope(Span span, Scope scope) { } +} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java new file mode 100644 index 00000000..3dbbf577 --- /dev/null +++ b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.queue; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import org.traffichunter.javaagent.trace.dto.TraceInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public enum TraceQueue { + + INSTANCE, + ; + + private final BlockingQueue syncQ = new LinkedBlockingQueue<>(100); + + /** + * this method is non-blocking + * @return success : true, fail : false + */ + public boolean add(final TraceInfo trInfo) { + return syncQ.offer(trInfo); + } + + /** + * this method is blocking + */ + public TraceInfo poll() throws InterruptedException { + return syncQ.take(); + } + + public void removeAll() { + syncQ.clear(); + } + + public int size() { + return syncQ.size(); + } +} diff --git a/java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-websocket/gradlew.bat b/java-agent/java-agent-websocket/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/java-agent/java-agent-websocket/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java new file mode 100644 index 00000000..1c360658 --- /dev/null +++ b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java @@ -0,0 +1,163 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.websocket; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.net.URI; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; + +/** + * The {@code MetricWebSocketClient} class extends {@link WebSocketClient} + * to provide specialized functionality for sending serialized metrics over a WebSocket connection. + * + *

Features:

+ *
    + *
  • Automatically sends metadata when the connection is opened.
  • + *
  • Supports sending metrics as JSON or compressed binary data.
  • + *
  • Provides utility methods to check and manage WebSocket connection state.
  • + *
+ * + * @see WebSocketClient + * @see SerializationByteArrayConverter + * @see AgentMetadata + * @see MetricType + * + * @author yungwang-o + * @version 1.0.0 + */ +public class MetricWebSocketClient extends WebSocketClient { + + private static final Logger log = LoggerFactory.getLogger(MetricWebSocketClient.class); + + private final SerializationByteArrayConverter converter; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final AgentMetadata metadata; + + public MetricWebSocketClient(final URI serverUri, final AgentMetadata metadata) { + super(serverUri); + this.metadata = metadata; + this.objectMapper + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .registerModule(new JavaTimeModule()); + this.converter = new SerializationByteArrayConverter(objectMapper); + } + + @Override + public void onOpen(final ServerHandshake serverHandshake) { + log.info("websocket client opened"); + this.toSend(metadata); + } + + @Override + public void onMessage(final String s) { + log.info("websocket client received = {}", s); + } + + + @Override + public void onClose(final int i, final String s, final boolean b) { + log.info("websocket client closed"); + } + + @Override + public void onError(final Exception e) { + log.error("websocket client error = {}", e.getMessage()); + } + + public boolean isConnected() { + if(isOpen()) { + return true; + } + + try { + return super.connectBlocking(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return false; + } + + public boolean isConnected(final long timeOut, final TimeUnit timeUnit) { + if(isOpen()) { + return true; + } + + try { + return super.connectBlocking(timeOut, timeUnit); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return false; + } + + public void toSend(final M metric) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + try { + String s = objectMapper.writeValueAsString(metric); + this.send(s); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public void compressToSend(final M metric, final MetricType metricType) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + byte[] transform = converter.transform(metric, metricType); + + this.send(transform); + } + + public void toSend(final List metrics) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + try { + this.send(objectMapper.writeValueAsString(metrics)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java new file mode 100644 index 00000000..0299e616 --- /dev/null +++ b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java @@ -0,0 +1,133 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.websocket.converter; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; + +/** + * The {@code SerializationByteArrayConverter} class provides utility methods for + * serializing objects into compressed byte arrays and deserializing compressed byte arrays + * back into objects. This is useful for efficient data transmission or storage. + * + *

Key Features:

+ *
    + *
  • Serializes objects to compressed byte arrays with a metric type identifier.
  • + *
  • Deserializes compressed byte arrays back into objects of specified types.
  • + *
  • Supports gzip compression for optimized size reduction.
  • + *
+ * + *

Usage:

+ *
    + *
  • {@code transform(Object, MetricType)}: Converts an object to a compressed byte array.
  • + *
  • {@code inverseTransform(byte[], TypeReference)}: Converts a compressed byte array back into an object.
  • + *
+ * + * @see ObjectMapper + * @see GZIPOutputStream + * @see GZIPInputStream + * @author yungwang-o + * @version 1.0.0 + */ +public class SerializationByteArrayConverter { + + private final ObjectMapper objectMapper; + + public SerializationByteArrayConverter(final ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public byte[] transform(final Object obj, final MetricType metricType) { + byte[] data = serialize(obj); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ + + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) { + gzipOutputStream.write(data); + } + + byte[] compressByteArray = baos.toByteArray(); + byte[] result = new byte[baos.toByteArray().length + 1]; + + result[0] = metricType.value; + + System.arraycopy(compressByteArray, 0, result, 1, compressByteArray.length); + + return result; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public MetadataWrapper inverseTransform(final byte[] data, + final TypeReference> typeReference) { + + byte[] copy = new byte[data.length - 1]; + + System.arraycopy(data, 1, copy, 0, copy.length); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try (GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(copy))) { + byte[] result = new byte[2048]; + + int len; + while ((len = gzipInputStream.read(result)) != -1) { + baos.write(result, 0, len); + } + + byte[] byteArray = baos.toByteArray(); + + return objectMapper.readValue(byteArray, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private byte[] serialize(final Object object) { + try { + return objectMapper.writeValueAsBytes(object); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public enum MetricType { + SYSTEM_METRIC((byte) 1), + TRANSACTION_METRIC((byte) 2), + ; + + private final byte value; + + MetricType(final byte value) { + this.value = value; + } + } +} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java new file mode 100644 index 00000000..e88902ff --- /dev/null +++ b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java @@ -0,0 +1,10 @@ +package org.traffichunter.javaagent.plugin.sdk.constant; + +public enum PluginConstant { + + SPRING_BOOT, + MYSQL, + KAFKA, + ELASTICSEARCH, + POSTGRES +} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java new file mode 100644 index 00000000..e9ef6630 --- /dev/null +++ b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java @@ -0,0 +1,9 @@ +package org.traffichunter.javaagent.plugin.sdk.instrumentation; + +import net.bytebuddy.agent.builder.AgentBuilder; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.type.TypeInstrumentation; + +public interface PluginInstrumentation extends TypeInstrumentation { + + void transform(AgentBuilder.Transformer transformer, ClassLoader classLoader); +} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java new file mode 100644 index 00000000..730431dc --- /dev/null +++ b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java @@ -0,0 +1,16 @@ +package org.traffichunter.javaagent.plugin.sdk.instrumentation.type; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; + +public interface TypeInstrumentation { + + default ElementMatcher.Junction classLoaderMatcher() { return any(); } + + ElementMatcher typeMatcher(); + + Junction ignorePackage(); +} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java new file mode 100644 index 00000000..1ff60c68 --- /dev/null +++ b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java @@ -0,0 +1,8 @@ +package org.traffichunter.javaagent.plugin.sdk.loader; + +import java.util.List; + +public interface PluginLoader

{ + + List

loadModules(ClassLoader classLoader); +} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java new file mode 100644 index 00000000..fa750825 --- /dev/null +++ b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java @@ -0,0 +1,23 @@ +package org.traffichunter.javaagent.plugin.sdk.loader; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; + +public class TrafficHunterPluginLoader implements PluginLoader { + + @Override + public List loadModules(final ClassLoader classLoader) { + + List loadPlugIn = new ArrayList<>(); + + ServiceLoader loader = ServiceLoader.load(PluginInstrumentation.class, classLoader); + + for(PluginInstrumentation plugIn : loader) { + loadPlugIn.add(plugIn); + } + + return loadPlugIn; + } +} diff --git a/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java b/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java new file mode 100644 index 00000000..37c0c7bd --- /dev/null +++ b/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java @@ -0,0 +1,36 @@ +package org.traffichunter.javaagent.plugin.spring.webmvc; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; + +public class SpringWebMvcPluginInstrumentation implements PluginInstrumentation { + + @Override + public void transform(final Transformer transformer, final ClassLoader classLoader) { + + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.springframework.web.servlet.DispatcherServlet"); + } + + @Override + public Junction ignorePackage() { + return null; + } + + public static class SpringWebMvcDispatcherServletAdvice { + + @OnMethodEnter + public static void enter() { + + } + } +} diff --git a/settings.gradle b/settings.gradle index e85a6d31..e5de471d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,12 @@ rootProject.name = 'traffic_hunter' -include('java-agent', 'server') \ No newline at end of file +include(':java-agent') +include(':server') +include(':java-agent:plugin') +include(':java-agent:plugin:plugin-sdk') +include(':java-agent:plugin:spring-webmvc') +include(':java-agent:java-agent-trace') +include(':java-agent:java-agent-retry') +include(':java-agent:java-agent-websocket') +include(':java-agent:java-agent-commons') +include(':java-agent:java-agent-bootstrap') +include(':java-agent:java-agent-event') \ No newline at end of file From 23e7d42f944f078b3c6f3c6d93b1739ef068b32e Mon Sep 17 00:00:00 2001 From: swager253 Date: Sat, 4 Jan 2025 17:16:47 +0900 Subject: [PATCH 03/25] =?UTF-8?q?(#22)=20javaagent=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java-agent/java-agent-bootstrap/build.gradle | 54 ++++ .../java-agent-bootstrap/settings.gradle | 2 + java-agent/java-agent-commons/build.gradle | 21 ++ java-agent/java-agent-commons/settings.gradle | 2 + java-agent/java-agent-event/build.gradle | 21 ++ java-agent/java-agent-event/settings.gradle | 2 + java-agent/java-agent-retry/build.gradle | 21 ++ java-agent/java-agent-retry/settings.gradle | 2 + java-agent/java-agent-trace/build.gradle | 24 ++ java-agent/java-agent-trace/settings.gradle | 2 + java-agent/java-agent-websocket/build.gradle | 26 ++ .../java-agent-websocket/settings.gradle | 2 + java-agent/plugin/build.gradle | 28 ++ .../plugin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + java-agent/plugin/gradlew | 249 ++++++++++++++++++ java-agent/plugin/gradlew.bat | 92 +++++++ java-agent/plugin/plugin-sdk/build.gradle | 18 ++ java-agent/plugin/settings.gradle | 1 + java-agent/plugin/spring-webmvc/build.gradle | 18 ++ 20 files changed, 592 insertions(+) create mode 100644 java-agent/java-agent-bootstrap/build.gradle create mode 100644 java-agent/java-agent-bootstrap/settings.gradle create mode 100644 java-agent/java-agent-commons/build.gradle create mode 100644 java-agent/java-agent-commons/settings.gradle create mode 100644 java-agent/java-agent-event/build.gradle create mode 100644 java-agent/java-agent-event/settings.gradle create mode 100644 java-agent/java-agent-retry/build.gradle create mode 100644 java-agent/java-agent-retry/settings.gradle create mode 100644 java-agent/java-agent-trace/build.gradle create mode 100644 java-agent/java-agent-trace/settings.gradle create mode 100644 java-agent/java-agent-websocket/build.gradle create mode 100644 java-agent/java-agent-websocket/settings.gradle create mode 100644 java-agent/plugin/build.gradle create mode 100644 java-agent/plugin/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-agent/plugin/gradle/wrapper/gradle-wrapper.properties create mode 100755 java-agent/plugin/gradlew create mode 100644 java-agent/plugin/gradlew.bat create mode 100644 java-agent/plugin/plugin-sdk/build.gradle create mode 100644 java-agent/plugin/settings.gradle create mode 100644 java-agent/plugin/spring-webmvc/build.gradle diff --git a/java-agent/java-agent-bootstrap/build.gradle b/java-agent/java-agent-bootstrap/build.gradle new file mode 100644 index 00000000..a6d2c63c --- /dev/null +++ b/java-agent/java-agent-bootstrap/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '8.1.1' +} + +group = 'org.traffichunter.javaagent.bootstrap' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +shadowJar { + + manifest { + attributes( + 'Premain-Class': 'org.traffichunter.javaagent.bootstrap.BootstrapMain', + 'Can-Redefine-Classes': 'true', + 'Can-Retransform-Classes': 'true', + 'Permissions': 'all-permissions' + ) + } + + archiveFileName.set("traffic-hunter-agent-v1.0.0.jar") +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-agent:java-agent-event') + implementation project(':java-agent:java-agent-commons') + implementation project(':java-agent:java-agent-websocket') + implementation project(':java-agent:java-agent-trace') + implementation project(':java-agent:java-agent-retry') + implementation project(':java-agent:plugin') + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' + + implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-simple:2.0.7' + implementation 'org.yaml:snakeyaml:2.3' + implementation 'org.java-websocket:Java-WebSocket:1.5.7' + implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' + implementation 'net.bytebuddy:byte-buddy:1.15.5' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-bootstrap/settings.gradle b/java-agent/java-agent-bootstrap/settings.gradle new file mode 100644 index 00000000..b0093ce4 --- /dev/null +++ b/java-agent/java-agent-bootstrap/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-bootstrap' + diff --git a/java-agent/java-agent-commons/build.gradle b/java-agent/java-agent-commons/build.gradle new file mode 100644 index 00000000..9578d4b7 --- /dev/null +++ b/java-agent/java-agent-commons/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.commons' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-agent:java-agent-event') +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-commons/settings.gradle b/java-agent/java-agent-commons/settings.gradle new file mode 100644 index 00000000..40b4423e --- /dev/null +++ b/java-agent/java-agent-commons/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-commons' + diff --git a/java-agent/java-agent-event/build.gradle b/java-agent/java-agent-event/build.gradle new file mode 100644 index 00000000..77a6a43b --- /dev/null +++ b/java-agent/java-agent-event/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.event' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-agent:java-agent-commons') +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-event/settings.gradle b/java-agent/java-agent-event/settings.gradle new file mode 100644 index 00000000..2946bf5b --- /dev/null +++ b/java-agent/java-agent-event/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-event' + diff --git a/java-agent/java-agent-retry/build.gradle b/java-agent/java-agent-retry/build.gradle new file mode 100644 index 00000000..c31598f4 --- /dev/null +++ b/java-agent/java-agent-retry/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.retry' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-retry/settings.gradle b/java-agent/java-agent-retry/settings.gradle new file mode 100644 index 00000000..1e64f5a3 --- /dev/null +++ b/java-agent/java-agent-retry/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-retry' + diff --git a/java-agent/java-agent-trace/build.gradle b/java-agent/java-agent-trace/build.gradle new file mode 100644 index 00000000..e45495c6 --- /dev/null +++ b/java-agent/java-agent-trace/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.trace' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-agent:java-agent-commons') + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-trace/settings.gradle b/java-agent/java-agent-trace/settings.gradle new file mode 100644 index 00000000..98816c44 --- /dev/null +++ b/java-agent/java-agent-trace/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-trace' + diff --git a/java-agent/java-agent-websocket/build.gradle b/java-agent/java-agent-websocket/build.gradle new file mode 100644 index 00000000..75d76e43 --- /dev/null +++ b/java-agent/java-agent-websocket/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.websocket' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-agent:java-agent-commons') + + implementation 'org.java-websocket:Java-WebSocket:1.5.7' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/java-agent-websocket/settings.gradle b/java-agent/java-agent-websocket/settings.gradle new file mode 100644 index 00000000..7b462378 --- /dev/null +++ b/java-agent/java-agent-websocket/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-agent-websocket' + diff --git a/java-agent/plugin/build.gradle b/java-agent/plugin/build.gradle new file mode 100644 index 00000000..418f1ce6 --- /dev/null +++ b/java-agent/plugin/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +subprojects { + apply plugin: 'java' + + dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation 'net.bytebuddy:byte-buddy:1.15.5' + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/plugin/gradle/wrapper/gradle-wrapper.jar b/java-agent/plugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties b/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1af9e093 --- /dev/null +++ b/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/java-agent/plugin/gradlew b/java-agent/plugin/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/java-agent/plugin/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-agent/plugin/gradlew.bat b/java-agent/plugin/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/java-agent/plugin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-agent/plugin/plugin-sdk/build.gradle b/java-agent/plugin/plugin-sdk/build.gradle new file mode 100644 index 00000000..a121ab7a --- /dev/null +++ b/java-agent/plugin/plugin-sdk/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin.plugin-sdk' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-agent/plugin/settings.gradle b/java-agent/plugin/settings.gradle new file mode 100644 index 00000000..1788b92f --- /dev/null +++ b/java-agent/plugin/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'plugin' \ No newline at end of file diff --git a/java-agent/plugin/spring-webmvc/build.gradle b/java-agent/plugin/spring-webmvc/build.gradle new file mode 100644 index 00000000..6ecb28b5 --- /dev/null +++ b/java-agent/plugin/spring-webmvc/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin.spring-webmvc' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':java-agent:plugin:plugin-sdk') +} + +test { + useJUnitPlatform() +} \ No newline at end of file From 3a040cfb59f8debecc5cebca02c6e112d690ed24 Mon Sep 17 00:00:00 2001 From: swager253 Date: Sun, 5 Jan 2025 16:20:56 +0900 Subject: [PATCH 04/25] =?UTF-8?q?(#22)=20java-apm-agent=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=EB=A1=9C=20=EC=9D=B4=EA=B4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java-agent-bootstrap/settings.gradle | 2 - .../bootstrap/banner/AsciiBanner.java | 2 +- .../engine/AgentExecutionEngine.java | 2 +- .../ConfigurableContextInitializer.java | 2 +- .../bootstrap/engine/sender/MetricSender.java | 2 +- .../manager/MetricSendSessionManager.java | 19 +- .../websocket/AgentSystemMetricSender.java | 4 +- .../AgentTransactionMetricSender.java | 4 +- .../bootstrap}/metadata/AgentMetadata.java | 2 +- .../bootstrap}/metadata/MetadataWrapper.java | 2 +- .../src/main/resources/agent-banner.txt | 11 + java-agent/java-agent-commons/build.gradle | 2 - java-agent/java-agent-commons/settings.gradle | 2 - java-agent/java-agent-event/settings.gradle | 2 - java-agent/java-agent-retry/settings.gradle | 2 - java-agent/java-agent-trace/settings.gradle | 2 - .../java-agent-websocket/settings.gradle | 2 - .../websocket/MetricWebSocketClient.java | 8 +- .../SerializationByteArrayConverter.java | 28 --- .../websocket/metadata/Metadata.java | 53 ++++ java-agent/plugin/settings.gradle | 1 - java-agent/settings.gradle | 1 - java-apm-agent/build.gradle | 17 ++ .../java-agent-bootstrap/build.gradle | 57 +++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../javaagent/bootstrap/BootstrapMain.java | 58 +++++ .../bootstrap/banner/AsciiBanner.java | 65 +++++ .../engine/AgentExecutionEngine.java | 232 ++++++++++++++++++ .../TrafficHunterAgentShutdownHook.java | 79 ++++++ .../context/AgentExecutableContext.java | 68 +++++ .../ConfigurableContextInitializer.java | 189 ++++++++++++++ .../TrafficHunterAgentExecutableContext.java | 191 ++++++++++++++ .../engine/env/ConfigurableEnvironment.java | 52 ++++ .../bootstrap/engine/env/Environment.java | 54 ++++ .../env/yaml/YamlConfigurableEnvironment.java | 120 +++++++++ .../env/yaml/bind/RelaxedBindingUtils.java | 59 +++++ .../env/yaml/root/RootYamlProperty.java | 50 ++++ .../env/yaml/root/agent/AgentSubProperty.java | 102 ++++++++ .../root/agent/retry/RetrySubProperty.java | 68 +++++ .../retry/backoff/BackOffSubProperty.java | 58 +++++ .../instrument/annotation/AnnotationPath.java | 57 +++++ .../instrument/bootstrap/BootState.java | 46 ++++ .../classloading/THAgentClassLoader.java | 59 +++++ .../instrument/locator/AgentLocator.java | 59 +++++ .../bootstrap/engine/lifecycle/LifeCycle.java | 88 +++++++ .../engine/property/TrafficHunterAgent.java | 122 +++++++++ .../property/TrafficHunterAgentProperty.java | 86 +++++++ .../FaultTolerantTrafficHunterAgent.java | 89 +++++++ .../bootstrap/engine/sender/MetricSender.java | 41 ++++ .../manager/MetricSendSessionManager.java | 198 +++++++++++++++ .../websocket/AgentSystemMetricSender.java | 77 ++++++ .../AgentTransactionMetricSender.java | 87 +++++++ .../bootstrap/metadata/AgentMetadata.java | 86 +++++++ .../bootstrap/metadata/MetadataWrapper.java | 35 +++ .../src/main/resources/agent-banner.txt | 11 + .../java-agent-commons/build.gradle | 19 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../javaagent/commons/status/AgentStatus.java | 34 +++ .../javaagent/commons/util/AgentUtil.java | 60 +++++ .../javaagent/commons/util/FileUtils.java | 48 ++++ .../javaagent/commons/util/UUIDGenerator.java | 82 +++++++ java-apm-agent/java-agent-event/build.gradle | 21 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../javaagent/event/AgentEvent.java | 54 ++++ .../AgentContextStateEventHandler.java | 43 ++++ .../listener/AgentStateEventListener.java | 36 +++ .../event/object/AgentStateEvent.java | 66 +++++ .../event/store/AgentStateEventStore.java | 74 ++++++ java-apm-agent/java-agent-jmx/build.gradle | 19 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../collect/AbstractMBeanMetricCollector.java | 94 +++++++ .../jmx/collect/MetricCollectSupport.java | 130 ++++++++++ .../jmx/collect/MetricCollector.java | 54 ++++ .../dbcp/hikari/HikariCPMetricCollector.java | 58 +++++ .../systeminfo/cpu/CpuMetricCollector.java | 72 ++++++ .../gc/GarbageCollectionMetricCollector.java | 74 ++++++ .../memory/MemoryMetricCollector.java | 97 ++++++++ .../runtime/RuntimeMetricCollector.java | 74 ++++++ .../thread/ThreadMetricCollector.java | 72 ++++++ .../web/tomcat/TomcatMetricCollector.java | 84 +++++++ .../javaagent/jmx/jvm/JVMSelector.java | 86 +++++++ .../jmx/metric/dbcp/HikariDbcpInfo.java | 37 +++ .../jmx/metric/systeminfo/SystemInfo.java | 49 ++++ .../metric/systeminfo/cpu/CpuStatusInfo.java | 31 +++ .../gc/GarbageCollectionStatusInfo.java | 34 +++ .../gc/collections/GarbageCollectionTime.java | 40 +++ .../systeminfo/memory/MemoryStatusInfo.java | 33 +++ .../systeminfo/runtime/RuntimeStatusInfo.java | 36 +++ .../systeminfo/thread/ThreadStatusInfo.java | 35 +++ .../web/tomcat/TomcatWebServerInfo.java | 37 +++ .../web/tomcat/request/TomcatRequestInfo.java | 37 +++ .../tomcat/thread/TomcatThreadPoolInfo.java | 35 +++ java-apm-agent/java-agent-retry/build.gradle | 21 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../javaagent/retry/RetryHelper.java | 169 +++++++++++++ .../retry/backoff/BackOffPolicy.java | 65 +++++ .../policy/ExponentialBackOffPolicy.java | 39 +++ .../backoff/policy/FixedBackOffPolicy.java | 39 +++ java-apm-agent/java-agent-trace/build.gradle | 24 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../javaagent/trace/dto/TraceInfo.java | 85 +++++++ .../trace/exporter/TraceExporter.java | 78 ++++++ .../javaagent/trace/manager/TraceManager.java | 72 ++++++ .../javaagent/trace/queue/TraceQueue.java | 63 +++++ .../java-agent-websocket/build.gradle | 26 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../websocket/MetricWebSocketClient.java | 163 ++++++++++++ .../SerializationByteArrayConverter.java | 105 ++++++++ .../websocket/metadata/Metadata.java | 53 ++++ java-apm-agent/plugin/build.gradle | 28 +++ .../plugin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + java-apm-agent/plugin/plugin-sdk/build.gradle | 18 ++ .../plugin/sdk/constant/PluginConstant.java | 10 + .../PluginInstrumentation.java | 9 + .../type/TypeInstrumentation.java | 16 ++ .../plugin/sdk/loader/PluginLoader.java | 8 + .../sdk/loader/TrafficHunterPluginLoader.java | 23 ++ .../plugin/spring-webmvc/build.gradle | 18 ++ .../SpringWebMvcPluginInstrumentation.java | 36 +++ java-apm-agent/versions.gradle | 3 + settings.gradle | 26 +- 129 files changed, 5846 insertions(+), 70 deletions(-) delete mode 100644 java-agent/java-agent-bootstrap/settings.gradle rename java-agent/{java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto => java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap}/metadata/AgentMetadata.java (98%) rename java-agent/{java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto => java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap}/metadata/MetadataWrapper.java (96%) create mode 100644 java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt delete mode 100644 java-agent/java-agent-commons/settings.gradle delete mode 100644 java-agent/java-agent-event/settings.gradle delete mode 100644 java-agent/java-agent-retry/settings.gradle delete mode 100644 java-agent/java-agent-trace/settings.gradle delete mode 100644 java-agent/java-agent-websocket/settings.gradle create mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java delete mode 100644 java-agent/plugin/settings.gradle delete mode 100644 java-agent/settings.gradle create mode 100644 java-apm-agent/build.gradle create mode 100644 java-apm-agent/java-agent-bootstrap/build.gradle create mode 100644 java-apm-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt create mode 100644 java-apm-agent/java-agent-commons/build.gradle create mode 100644 java-apm-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java create mode 100644 java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java create mode 100644 java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java create mode 100644 java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java create mode 100644 java-apm-agent/java-agent-event/build.gradle create mode 100644 java-apm-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-event/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java create mode 100644 java-apm-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java create mode 100644 java-apm-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java create mode 100644 java-apm-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java create mode 100644 java-apm-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java create mode 100644 java-apm-agent/java-agent-jmx/build.gradle create mode 100644 java-apm-agent/java-agent-jmx/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-jmx/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/AbstractMBeanMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollectSupport.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/dbcp/hikari/HikariCPMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/cpu/CpuMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/gc/GarbageCollectionMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/memory/MemoryMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/runtime/RuntimeMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/thread/ThreadMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/web/tomcat/TomcatMetricCollector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/jvm/JVMSelector.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/dbcp/HikariDbcpInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/SystemInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/cpu/CpuStatusInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/GarbageCollectionStatusInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/collections/GarbageCollectionTime.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/memory/MemoryStatusInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/runtime/RuntimeStatusInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/thread/ThreadStatusInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/TomcatWebServerInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/request/TomcatRequestInfo.java create mode 100644 java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/thread/TomcatThreadPoolInfo.java create mode 100644 java-apm-agent/java-agent-retry/build.gradle create mode 100644 java-apm-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java create mode 100644 java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java create mode 100644 java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java create mode 100644 java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java create mode 100644 java-apm-agent/java-agent-trace/build.gradle create mode 100644 java-apm-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java create mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java create mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java create mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java create mode 100644 java-apm-agent/java-agent-websocket/build.gradle create mode 100644 java-apm-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java create mode 100644 java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java create mode 100644 java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java create mode 100644 java-apm-agent/plugin/build.gradle create mode 100644 java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/plugin/plugin-sdk/build.gradle create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java create mode 100644 java-apm-agent/plugin/spring-webmvc/build.gradle create mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java create mode 100644 java-apm-agent/versions.gradle diff --git a/java-agent/java-agent-bootstrap/settings.gradle b/java-agent/java-agent-bootstrap/settings.gradle deleted file mode 100644 index b0093ce4..00000000 --- a/java-agent/java-agent-bootstrap/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-bootstrap' - diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java index 95ad86b1..3d27b91b 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java @@ -31,7 +31,7 @@ import java.time.format.DateTimeFormatter; import java.util.Objects; import java.util.stream.Collectors; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; /** * banner print. diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java index 45ed3993..1761c79d 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java @@ -37,7 +37,7 @@ import org.traffichunter.javaagent.bootstrap.engine.lifecycle.LifeCycle; import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.trace.manager.TraceManager; import org.traffichunter.javaagent.trace.queue.TraceQueue; diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java index 342c0180..197a32a3 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java @@ -55,7 +55,7 @@ import org.traffichunter.javaagent.bootstrap.engine.env.Environment; import org.traffichunter.javaagent.bootstrap.engine.instrument.annotation.AnnotationPath; import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.commons.util.UUIDGenerator; import org.traffichunter.javaagent.trace.exporter.TraceExporter; diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java index 8e822c49..40f9ccd3 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java @@ -25,7 +25,7 @@ import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; /** * The {@code MetricSender} interface defines the contract for sending metrics. diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java index 328e3128..532cb0b4 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java @@ -35,11 +35,12 @@ import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.commons.util.AgentUtil; import org.traffichunter.javaagent.retry.RetryHelper; import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.metadata.Metadata; /** * The {@code MetricSendSessionManager} class manages the session for sending @@ -109,7 +110,7 @@ public MetricSendSessionManager(final TrafficHunterAgentProperty property, final AgentExecutableContext context, final AgentMetadata metadata) { - this.client = new MetricWebSocketClient(AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), metadata); + this.client = initializeWebsocket(property, metadata); this.client.connect(); this.metadata = metadata; this.context = context; @@ -171,6 +172,20 @@ public void close() { schedule.shutdown(); } + private static MetricWebSocketClient initializeWebsocket(final TrafficHunterAgentProperty property, + final AgentMetadata metadata) { + return new MetricWebSocketClient( + AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), + Metadata.builder() + .agentId(metadata.agentId()) + .agentVersion(metadata.agentVersion()) + .agentName(metadata.agentName()) + .startTime(metadata.startTime()) + .status(metadata.status().get()) + .build() + ); + } + private ThreadFactory getThreadFactory(final String threadName) { return r -> { Thread thread = new Thread(r); diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java index 5ae34f94..eb535e22 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java @@ -28,8 +28,8 @@ import org.traffichunter.javaagent.bootstrap.engine.collect.MetricCollectSupport; import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; import org.traffichunter.javaagent.commons.dto.systeminfo.SystemInfo; import org.traffichunter.javaagent.websocket.MetricWebSocketClient; import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java index 74ef2345..1a39c5c2 100644 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java @@ -26,8 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; import org.traffichunter.javaagent.trace.dto.TraceInfo; import org.traffichunter.javaagent.trace.queue.TraceQueue; import org.traffichunter.javaagent.websocket.MetricWebSocketClient; diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java similarity index 98% rename from java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java rename to java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java index 4850d399..3b65d6cb 100644 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/AgentMetadata.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.commons.dto.metadata; +package org.traffichunter.javaagent.bootstrap.metadata; import java.time.Instant; import java.util.Objects; diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java similarity index 96% rename from java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java rename to java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java index 0d626faf..64b9bd93 100644 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/metadata/MetadataWrapper.java +++ b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.commons.dto.metadata; +package org.traffichunter.javaagent.bootstrap.metadata; /** * @author yungwang-o diff --git a/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt b/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt new file mode 100644 index 00000000..adb4b4ec --- /dev/null +++ b/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt @@ -0,0 +1,11 @@ +_____ ___________________ ______ _____ +__ /_____________ ___ __/__ __/__(_)______ ___ /_____ __________ /_____________ +_ __/_ ___/ __ `/_ /_ __ /_ __ /_ ___/ __ __ \ / / /_ __ \ __/ _ \_ ___/ +/ /_ _ / / /_/ /_ __/ _ __/ _ / / /__ _ / / / /_/ /_ / / / /_ / __/ / +\__/ /_/ \__,_/ /_/ /_/ /_/ \___/ /_/ /_/\__,_/ /_/ /_/\__/ \___//_/ + +:: Traffic Hunter ${version} Ver :: +:: Made by ygo :: +:: Running on Java ${java.version} :: +:: JDK Information ${jdk} ${java.specification} :: +:: Started on ${time} :: \ No newline at end of file diff --git a/java-agent/java-agent-commons/build.gradle b/java-agent/java-agent-commons/build.gradle index 9578d4b7..c2688fc2 100644 --- a/java-agent/java-agent-commons/build.gradle +++ b/java-agent/java-agent-commons/build.gradle @@ -12,8 +12,6 @@ repositories { dependencies { testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-agent:java-agent-event') } test { diff --git a/java-agent/java-agent-commons/settings.gradle b/java-agent/java-agent-commons/settings.gradle deleted file mode 100644 index 40b4423e..00000000 --- a/java-agent/java-agent-commons/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-commons' - diff --git a/java-agent/java-agent-event/settings.gradle b/java-agent/java-agent-event/settings.gradle deleted file mode 100644 index 2946bf5b..00000000 --- a/java-agent/java-agent-event/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-event' - diff --git a/java-agent/java-agent-retry/settings.gradle b/java-agent/java-agent-retry/settings.gradle deleted file mode 100644 index 1e64f5a3..00000000 --- a/java-agent/java-agent-retry/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-retry' - diff --git a/java-agent/java-agent-trace/settings.gradle b/java-agent/java-agent-trace/settings.gradle deleted file mode 100644 index 98816c44..00000000 --- a/java-agent/java-agent-trace/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-trace' - diff --git a/java-agent/java-agent-websocket/settings.gradle b/java-agent/java-agent-websocket/settings.gradle deleted file mode 100644 index 7b462378..00000000 --- a/java-agent/java-agent-websocket/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'java-agent-websocket' - diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java index 1c360658..5178d488 100644 --- a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java +++ b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java @@ -34,9 +34,9 @@ import org.java_websocket.handshake.ServerHandshake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.commons.dto.metadata.AgentMetadata; import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter; import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; +import org.traffichunter.javaagent.websocket.metadata.Metadata; /** * The {@code MetricWebSocketClient} class extends {@link WebSocketClient} @@ -51,7 +51,7 @@ * * @see WebSocketClient * @see SerializationByteArrayConverter - * @see AgentMetadata + * @see Metadata * @see MetricType * * @author yungwang-o @@ -65,9 +65,9 @@ public class MetricWebSocketClient extends WebSocketClient { private final ObjectMapper objectMapper = new ObjectMapper(); - private final AgentMetadata metadata; + private final Metadata metadata; - public MetricWebSocketClient(final URI serverUri, final AgentMetadata metadata) { + public MetricWebSocketClient(final URI serverUri, final Metadata metadata) { super(serverUri); this.metadata = metadata; this.objectMapper diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java index 0299e616..33f2304a 100644 --- a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java +++ b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java @@ -23,14 +23,11 @@ */ package org.traffichunter.javaagent.websocket.converter; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import org.traffichunter.javaagent.commons.dto.metadata.MetadataWrapper; /** * The {@code SerializationByteArrayConverter} class provides utility methods for @@ -86,31 +83,6 @@ public byte[] transform(final Object obj, final MetricType metricType) { } } - public MetadataWrapper inverseTransform(final byte[] data, - final TypeReference> typeReference) { - - byte[] copy = new byte[data.length - 1]; - - System.arraycopy(data, 1, copy, 0, copy.length); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - try (GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(copy))) { - byte[] result = new byte[2048]; - - int len; - while ((len = gzipInputStream.read(result)) != -1) { - baos.write(result, 0, len); - } - - byte[] byteArray = baos.toByteArray(); - - return objectMapper.readValue(byteArray, typeReference); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - private byte[] serialize(final Object object) { try { return objectMapper.writeValueAsBytes(object); diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java new file mode 100644 index 00000000..78c25eed --- /dev/null +++ b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java @@ -0,0 +1,53 @@ +package org.traffichunter.javaagent.websocket.metadata; + +import java.time.Instant; +import org.traffichunter.javaagent.commons.status.AgentStatus; + +public record Metadata( + String agentId, + String agentVersion, + String agentName, + Instant startTime, + AgentStatus status) { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String agentId; + private String agentVersion; + private String agentName; + private Instant startTime; + private AgentStatus status; + + public Builder agentId(String agentId) { + this.agentId = agentId; + return this; + } + + public Builder agentVersion(String agentVersion) { + this.agentVersion = agentVersion; + return this; + } + + public Builder agentName(String agentName) { + this.agentName = agentName; + return this; + } + + public Builder startTime(Instant startTime) { + this.startTime = startTime; + return this; + } + + public Builder status(AgentStatus status) { + this.status = status; + return this; + } + + public Metadata build() { + return new Metadata(agentId, agentVersion, agentName, startTime, status); + } + } +} diff --git a/java-agent/plugin/settings.gradle b/java-agent/plugin/settings.gradle deleted file mode 100644 index 1788b92f..00000000 --- a/java-agent/plugin/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'plugin' \ No newline at end of file diff --git a/java-agent/settings.gradle b/java-agent/settings.gradle deleted file mode 100644 index a6e454cc..00000000 --- a/java-agent/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'java-agent' \ No newline at end of file diff --git a/java-apm-agent/build.gradle b/java-apm-agent/build.gradle new file mode 100644 index 00000000..f4a5be5d --- /dev/null +++ b/java-apm-agent/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent' +version = '0.0.1-SNAPSHOT' + +dependencies { +} + +jar { + enabled = false +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/build.gradle b/java-apm-agent/java-agent-bootstrap/build.gradle new file mode 100644 index 00000000..82872ea4 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/build.gradle @@ -0,0 +1,57 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '8.1.1' +} + +group = 'org.traffichunter.javaagent.bootstrap' +version = '1.0-SNAPSHOT' + +apply from: '../versions.gradle' + +repositories { + mavenCentral() +} + +shadowJar { + + manifest { + attributes( + 'Premain-Class': 'org.traffichunter.javaagent.bootstrap.BootstrapMain', + 'Can-Redefine-Classes': 'true', + 'Can-Retransform-Classes': 'true', + 'Permissions': 'all-permissions' + ) + } + + archiveFileName.set("traffic-hunter-agent-${AgentReleaseVersion}.jar") +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-apm-agent:java-agent-event') + implementation project(':java-apm-agent:java-agent-commons') + implementation project(':java-apm-agent:java-agent-websocket') + implementation project(':java-apm-agent:java-agent-trace') + implementation project(':java-apm-agent:java-agent-retry') + implementation project(':java-apm-agent:java-agent-jmx') + implementation project(':java-apm-agent:plugin') + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' + + implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-simple:2.0.7' + implementation 'org.yaml:snakeyaml:2.3' + implementation 'org.java-websocket:Java-WebSocket:1.5.7' + implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' + implementation 'net.bytebuddy:byte-buddy:1.15.5' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 line + .replace("${version}", metadata.agentVersion()) + .replace("${java.version}", System.getProperty("java.version")) + .replace("${java.specification}", System.getProperty("java.specification.version")) + .replace("${jdk}", System.getProperty("java.vendor")) + .replace("${time}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))) + .collect(Collectors.joining(System.lineSeparator())); + + System.out.println(banner + "\n"); + + } catch (IOException ignored) { + + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java new file mode 100644 index 00000000..1761c79d --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java @@ -0,0 +1,232 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine; + +import java.lang.instrument.Instrumentation; +import java.time.Duration; +import java.time.Instant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.banner.AsciiBanner; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.context.execute.TrafficHunterAgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.lifecycle.LifeCycle; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.queue.TraceQueue; + +/** + *

+ * The {@code AgentExecutionEngine} class is the core execution engine responsible for + * initializing, configuring, running, and shutting down the TrafficHunter Agent. + * This class encapsulates all the necessary steps to bootstrap the agent, manage its + * lifecycle, and ensure proper cleanup upon application termination. + *

+ *

Purpose:

+ *
    + *
  • Initialize and configure the TrafficHunter Agent with environment-specific settings.
  • + *
  • Run the agent in a dedicated thread to monitor and manage traffic.
  • + *
  • Register and manage shutdown hooks to ensure proper cleanup during termination.
  • + *
+ * + *

Key Components:

+ *
    + *
  • {@link TrafficHunterAgentShutdownHook} - Manages graceful shutdown of resources.
  • + *
  • {@link ConfigurableEnvironment} - Loads environment-specific configurations.
  • + *
  • {@link AgentRunner} - Executes the agent's core logic in a separate thread.
  • + *
+ * + *

Limitations:

+ *
    + *
  • This class assumes that the {@code Instrumentation} object is correctly provided at runtime.
  • + *
  • Agent initialization failures may result in partial cleanup if not handled carefully.
  • + *
+ * + * @see TrafficHunterAgentShutdownHook + * @see AgentRunner + * @see ConfigurableEnvironment + * @see Instrumentation + * @author yungwang-o + * @version 1.0.0 + */ +public final class AgentExecutionEngine { + + private static final Logger log = LoggerFactory.getLogger(AgentExecutionEngine.class); + + private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); + + private final AsciiBanner asciiBanner = new AsciiBanner(); + + private final ConfigurableEnvironment environment; + + private final Instrumentation inst; + + private AgentExecutionEngine(final String args, final Instrumentation inst) { + this.inst = inst; + this.environment = new YamlConfigurableEnvironment(args); + } + + /** + * Initializes and executes the TrafficHunter Agent. + *

This method performs the following tasks:

+ *
    + *
  • Loads environment-specific configurations using {@link YamlConfigurableEnvironment}.
  • + *
  • Initializes the agent's execution context and metadata.
  • + *
  • Registers shutdown hooks for cleanup during termination.
  • + *
  • Starts the agent's core logic in a dedicated thread.
  • + *
+ */ + private void run() { + StartUp startUp = new StartUp(); + Instant startTime = startUp.getStartTime(); + final AgentExecutableContext context = new TrafficHunterAgentExecutableContext(environment, shutdownHook); + if(!shutdownHook.isEnabledShutdownHook()) { + shutdownHook.enableShutdownHook(); + } + ConfigurableContextInitializer configurableContextInitializer = context.init(); + configurableContextInitializer.retransform(inst); + TrafficHunterAgentProperty property = configurableContextInitializer.property(); + AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( + startTime, + AgentStatus.INITIALIZED + ); + context.addAgentStateEventListener(metadata); + asciiBanner.print(metadata); + AgentRunner runner = new AgentRunner(property, context, metadata); + Thread runnerThread = new Thread(runner); + runnerThread.setName("TrafficHunterAgentRunnerThread"); + if(context.isInit()) { + log.info("Agent initialization completed."); + runnerThread.start(); + registryShutdownHook(context, runner); + context.close(); + } + + log.info("Started TrafficHunter Agent in {} second", String.format("%.3f", startUp.getUpTime())); + } + + /** + * Registers shutdown hooks for the agent's cleanup operations. + * + * @param context The execution context of the agent. + * @param runner The agent runner instance responsible for managing execution. + */ + private void registryShutdownHook(final AgentExecutableContext context, final AgentRunner runner) { + shutdownHook.addRuntimeShutdownHook(TraceManager::close); + shutdownHook.addRuntimeShutdownHook(TraceQueue.INSTANCE::removeAll); + shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); + shutdownHook.addRuntimeShutdownHook(runner::close); + } + + public static void run(final String args, final Instrumentation inst) { + new AgentExecutionEngine(System.getProperty(args), inst).run(); + } + + /** + * The {@code StartUp} class extends {@link LifeCycle} to measure the agent's + * startup durations. + * + *

Features:

+ *
    + *
  • Tracks the agent's start time and end time.
  • + *
  • Calculates the total uptime.
  • + *
+ */ + private static class StartUp extends LifeCycle { + + public StartUp() { + super(); + } + + @Override + public Instant getStartTime() { + return this.startTime; + } + + @Override + public Instant getEndTime() { + if(endTime == null) { + this.endTime = Instant.now(); + } + return endTime; + } + + @Override + public Double getUpTime() { + if(getStartTime() == null && getEndTime() == null) { + throw new IllegalStateException("No start time or end time specified"); + } + + return Duration.between(getStartTime(), getEndTime()).toMillis() / 1_000.0; + } + } + + /** + * The {@code AgentRunner} class implements {@link Runnable} to execute the core logic + * of the TrafficHunter Agent in a separate thread. It initializes the session manager + * and orchestrates agent operations after the target application is loaded. + * + *

Features:

+ *
    + *
  • Introduces a delay to ensure the target application is fully loaded before execution.
  • + *
  • Manages the lifecycle of the {@link MetricSendSessionManager}.
  • + *
+ */ + private static final class AgentRunner implements Runnable { + + private final MetricSendSessionManager sessionManager; + + public AgentRunner(final TrafficHunterAgentProperty property, + final AgentExecutableContext context, + final AgentMetadata metadata) { + + this.sessionManager = new MetricSendSessionManager(property, context, metadata); + } + + /** + * Executes the agent's core logic. Introduces a delay to ensure that + * the target application is fully loaded before starting. + */ + @Override + public void run() { + try { + log.info("Waiting for Agent Runner..."); + Thread.sleep(8000); + sessionManager.run(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public void close() { + sessionManager.close(); + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java new file mode 100644 index 00000000..5bec461a --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@code TrafficHunterAgentShutdownHook} class is responsible for managing and + * registering shutdown hooks to execute specific actions when the Java application + * terminates. This class allows users to add custom {@link Runnable} actions that + * will be executed gracefully during the shutdown process. + * + * @see Runtime#addShutdownHook(Thread) + * @see Runnable + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgentShutdownHook implements Runnable { + + private static final Logger log = LoggerFactory.getLogger(TrafficHunterAgentShutdownHook.class); + + private volatile boolean enabledShutdownHook = false; + + private final Set actions = ConcurrentHashMap.newKeySet(); + + public void enableShutdownHook() { + enabledShutdownHook = true; + } + + public void addRuntimeShutdownHook(final Runnable action) { + actions.add(action); + } + + @Override + public void run() { + log.info("register shutdown hook"); + + if(!enabledShutdownHook) { + log.info("shutdown hook not enabled"); + return; + } + + for(Runnable action : actions) { + Runtime.getRuntime().addShutdownHook(new Thread(action, nameShutdownThread(action.getClass().getName()))); + } + } + + public boolean isEnabledShutdownHook() { + return enabledShutdownHook; + } + + private static String nameShutdownThread(final String threadName) { + return threadName + "-ShutdownHook"; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java new file mode 100644 index 00000000..99d4b0c1 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context; + +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.context.AgentContextStateEventHandler; + +/** + * The {@code AgentExecutableContext} interface defines the contract for managing + * the lifecycle of an agent's execution context. It includes methods for initializing + * the context, managing its state, and handling lifecycle events. + * + *

Features:

+ *
    + *
  • Extends {@link AgentContextStateEventHandler} for state event listener management.
  • + *
  • Provides methods to initialize, close, and query the context's status.
  • + *
  • Supports dynamic updates to the agent's execution status.
  • + *
+ * + * @see AgentContextStateEventHandler + * @see ConfigurableContextInitializer + * @see ConfigurableEnvironment + * @see AgentStatus + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface AgentExecutableContext extends AgentContextStateEventHandler { + + ConfigurableContextInitializer init(); + + void close(); + + ConfigurableEnvironment getEnvironment(); + + AgentStatus getStatus(); + + boolean isInit(); + + boolean isRunning(); + + boolean isStopped(); + + void setStatus(AgentStatus status); +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java new file mode 100644 index 00000000..197a32a3 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java @@ -0,0 +1,189 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context.configuration; + +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import java.io.InputStream; +import java.lang.instrument.Instrumentation; +import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder.Default; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.Enter; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.asm.Advice.Origin; +import net.bytebuddy.asm.Advice.Thrown; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; +import net.bytebuddy.matcher.ElementMatchers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.Environment; +import org.traffichunter.javaagent.bootstrap.engine.instrument.annotation.AnnotationPath; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.commons.util.UUIDGenerator; +import org.traffichunter.javaagent.trace.exporter.TraceExporter; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +/** + * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, + * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. + * + *

Features:

+ *
    + *
  • Loads properties using a configurable environment.
  • + *
  • Sets up agent metadata based on the current environment and runtime status.
  • + *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • + *
+ * + * @see ConfigurableEnvironment + * @see TrafficHunterAgentProperty + * @see ByteBuddy + * @see AgentMetadata + * + * @author yungwang-o + * @version 1.0.0 + */ +public class ConfigurableContextInitializer { + + private static final Logger log = LoggerFactory.getLogger(ConfigurableContextInitializer.class); + + private final ConfigurableEnvironment env; + + public ConfigurableContextInitializer(final ConfigurableEnvironment env) { + this.env = env; + } + + public TrafficHunterAgentProperty property() { + return env.load(); + } + + public TrafficHunterAgentProperty property(final InputStream is) { + return env.load(is); + } + + public void retransform(final Instrumentation inst) { + new Default() + .ignore(ignoreMatchPackage()) + .type(getSpringComponentMatcher()) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> + builder.visit(Advice.to(TransactionAdvise.class).on(isMethod())) + ).installOn(inst); + } + + public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { + + final String agentName = property().name(); + + return new AgentMetadata( + UUIDGenerator.generate(agentName), + Environment.VERSION.version(), + agentName, + startTime, + new AtomicReference<>(status) + ); + } + + private Junction ignoreMatchPackage() { + return ElementMatchers.nameStartsWith("java.") + .or(ElementMatchers.nameStartsWith("sun.")) + .or(ElementMatchers.nameStartsWith("jdk.")); + } + + private ElementMatcher getSpringComponentMatcher() { + return isAnnotatedWith(named(AnnotationPath.SERVICE.getPath())) + .or(isAnnotatedWith(named(AnnotationPath.REST_CONTROLLER.getPath()))) + .or(isAnnotatedWith(named(AnnotationPath.CONTROLLER.getPath()))) + .or(isAnnotatedWith(named(AnnotationPath.REPOSITORY.getPath()))); + } + + /** + *

+ * Intercepts method execution to create a span for tracing. + * Handles span lifecycle, recording exceptions, and closing the scope. + *

+ * + *

Note: Ensure the class has a public access modifier to avoid + * visibility issues when used with external components or frameworks like ByteBuddy. + * Using a private or package-private access modifier may result in runtime errors + * due to restricted access.

+ * + * @see TraceManager + */ + public static class TransactionAdvise { + + public static final Tracer tracer = TraceManager.configure(new TraceExporter()); + + @OnMethodEnter + public static SpanScope enter(@Origin final String method) { + + Span currentSpan = Span.current(); + + Span span = tracer.spanBuilder(method) + .setParent(Context.current().with(currentSpan)) + .setAttribute("method.name", method) + .setStartTimestamp(Instant.now()) + .startSpan(); + + Scope scope = span.makeCurrent(); + + return new SpanScope(span, scope); + } + + @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + + if (Objects.nonNull(throwable)) { + + String exception = throwable.getClass().getName() + + " " + + "(" + + throwable.getMessage() + + ")"; + + spanScope.span().recordException(throwable); + spanScope.span().setStatus(StatusCode.ERROR, exception); + } + + spanScope.span().end(Instant.now()); + spanScope.scope().close(); + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java new file mode 100644 index 00000000..5d523642 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java @@ -0,0 +1,191 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.context.execute; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.traffichunter.javaagent.bootstrap.engine.TrafficHunterAgentShutdownHook; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; +import org.traffichunter.javaagent.event.object.AgentStateEvent; +import org.traffichunter.javaagent.event.store.AgentStateEventStore; + +/** + * The {@code TrafficHunterAgentExecutableContext} class represents the execution context + * for the TrafficHunter Agent. It manages the agent's state, event listeners, environment + * configuration, and shutdown operations. + * + *

Purpose:

+ *
    + *
  • Maintains the current state of the agent using an atomic {@link AgentStatus}.
  • + *
  • Registers and manages {@link AgentStateEventListener} instances for state change notifications.
  • + *
  • Integrates a shutdown mechanism using {@link TrafficHunterAgentShutdownHook}.
  • + *
+ * + *

Key Features:

+ *
    + *
  • {@code init()} - Initializes the agent with the provided environment settings.
  • + *
  • {@code addAgentStateEventListener()} - Adds a listener to observe state changes.
  • + *
  • {@code removeAllAgentStateEventListeners()} - Removes all registered listeners.
  • + *
  • {@code close()} - Safely shuts down the agent using a dedicated shutdown thread.
  • + *
  • {@code setStatus()} - Atomically updates the agent's state and notifies listeners of the change.
  • + *
+ * + *

Thread Safety:

+ *
    + *
  • The {@code status} is managed using {@link AtomicReference}, ensuring thread-safe updates.
  • + *
  • Shutdown operations are protected by a {@link ReentrantLock} to prevent concurrent execution.
  • + *
  • {@link AtomicBoolean} is used to ensure the shutdown logic executes only once.
  • + *
+ * + *

Limitations:

+ *
    + *
  • State listeners are invoked sequentially; long-running listeners may delay others.
  • + *
  • Shutdown operations rely on an additional thread, which may cause delays during JVM termination.
  • + *
+ * + * @see AgentExecutableContext + * @see AgentStatus + * @see TrafficHunterAgentShutdownHook + * @see ConfigurableContextInitializer + * @see AgentStateEventListener + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgentExecutableContext extends AgentStateEventStore implements AgentExecutableContext { + + private final ConfigurableEnvironment environment; + + private final AtomicReference status = new AtomicReference<>(AgentStatus.INITIALIZED); + + private final TrafficHunterAgentShutdownHook shutdownHook; + + private final ReentrantLock shutdownLock = new ReentrantLock(); + + private final AtomicBoolean isShutdown = new AtomicBoolean(false); + + public TrafficHunterAgentExecutableContext(final ConfigurableEnvironment environment, + final TrafficHunterAgentShutdownHook shutdownHook) { + this.environment = environment; + this.shutdownHook = shutdownHook; + } + + @Override + public void addAgentStateEventListener(final AgentStateEventListener listener) { + super.addAgentStateEventListener(listener); + } + + @Override + public void removeAgentStateEventListener(final AgentStateEventListener listener) { + super.removeAgentStateEventListener(listener); + } + + @Override + public void removeAllAgentStateEventListeners() { + super.removeAll(); + } + + @Override + public ConfigurableContextInitializer init() { + return new ConfigurableContextInitializer(environment); + } + + /** + * Safely shuts down the agent by executing the registered shutdown hooks. + *

Ensures that:

+ *
    + *
  • The shutdown hook is executed only once.
  • + *
  • Concurrent shutdown attempts are prevented using a {@link ReentrantLock}.
  • + *
+ * If the shutdown hook is not enabled, this method does nothing. + */ + @Override + public void close() { + + if(isStopped()) { + return; + } + + if(this.shutdownHook.isEnabledShutdownHook() && this.isShutdown.compareAndSet(false, true)) { + Thread shutdownHookThread; + shutdownLock.lock(); + try { + shutdownHookThread = new Thread(this.shutdownHook, "TrafficHunterAgentShutdownHook"); + shutdownHookThread.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + shutdownLock.unlock(); + } + } + } + + @Override + public ConfigurableEnvironment getEnvironment() { + return environment; + } + + @Override + public AgentStatus getStatus() { + return status.get(); + } + + @Override + public boolean isInit() { + return status.get() == AgentStatus.INITIALIZED; + } + + @Override + public boolean isRunning() { + return status.get() == AgentStatus.RUNNING; + } + + @Override + public boolean isStopped() { + return status.get() == AgentStatus.EXIT; + } + + @Override + public void setStatus(final AgentStatus newStatus) { + AgentStatus agentStatus; + + do { + agentStatus = status.get(); + } while (!status.compareAndSet(agentStatus, newStatus)); + + AgentStateEvent event = new AgentStateEvent(this, agentStatus, newStatus); + + notifyAgentStateChange(event); + } + + private void notifyAgentStateChange(final AgentStateEvent event) { + for(AgentStateEventListener listener : super.getListeners()) { + listener.onEvent(event); + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java new file mode 100644 index 00000000..0908db02 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env; + +import java.io.InputStream; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; + +/** + * The {@code ConfigurableEnvironment} interface defines the contract for loading + * agent configuration properties. It supports loading configuration from + * default or provided input sources. + * + *

Features:

+ *
    + *
  • Loads configuration properties into a {@link TrafficHunterAgentProperty} instance.
  • + *
  • Supports default and custom input streams for configuration sources.
  • + *
+ * + * @see TrafficHunterAgentProperty + * @see YamlConfigurableEnvironment + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface ConfigurableEnvironment { + + TrafficHunterAgentProperty load(); + + TrafficHunterAgentProperty load(InputStream is); +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java new file mode 100644 index 00000000..7c923b76 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public enum Environment { + + DEFAULT_PATH("/env/agent-env.yml"), + VERSION("1.0.0"), + SYSTEM_PROFILE("traffichunter.config"), + ; + + private final String env; + + Environment(final String env) { + this.env = env; + } + + public String path() { + return env; + } + + public String version() { + return env; + } + + public String systemProfile() { + return env; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java new file mode 100644 index 00000000..a19d6860 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml; + +import java.io.InputStream; +import java.util.concurrent.TimeUnit; +import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind.RelaxedBindingUtils; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.RootYamlProperty; +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.commons.util.FileUtils; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; +import org.traffichunter.javaagent.retry.backoff.policy.ExponentialBackOffPolicy; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +/** + * The {@code YamlConfigurableEnvironment} class implements {@link ConfigurableEnvironment} + * to load configuration properties from a YAML file or input stream. + * + *

Features:

+ *
    + *
  • Loads configuration properties from a YAML file or input stream.
  • + *
  • Parses YAML data into a {@link TrafficHunterAgentProperty} instance using + * custom relaxed binding rules.
  • + *
  • Supports a default configuration file or custom file paths.
  • + *
+ * + * @see ConfigurableEnvironment + * @see TrafficHunterAgentProperty + * + * @author yungwang-o + * @version 1.0.0 + */ +public class YamlConfigurableEnvironment implements ConfigurableEnvironment { + + private static final String DEFAULT_CONFIG_FILE = "agent-env.yml"; + + private final Yaml yaml; + + private final String path; + + public YamlConfigurableEnvironment(final String path) { + final Constructor constructor = new Constructor(RootYamlProperty.class, new LoaderOptions()); + + constructor.setPropertyUtils(new RelaxedBindingUtils()); + + this.yaml = new Yaml(constructor); + this.path = path; + } + + @Override + public TrafficHunterAgentProperty load() { + final InputStream is = FileUtils.getFile(path); + + final RootYamlProperty root = yaml.load(is); + + return YamlParser.parse(root); + } + + @Override + public TrafficHunterAgentProperty load(final InputStream is) { + + final RootYamlProperty root = yaml.load(is); + + return YamlParser.parse(root); + } + + static class YamlParser { + + private static TrafficHunterAgentProperty parse(final RootYamlProperty root){ + + return TrafficHunterAgent.connect(root.getAgent().getServerUri()) + .name(root.getAgent().getName()) + .targetUri(root.getAgent().getTargetUri()) + .locationJar(root.getAgent().getJar()) + .scheduleInterval(root.getAgent().getInterval()) + .scheduleTimeUnit(TimeUnit.SECONDS) + .faultTolerant() + .retry(root.getAgent().getRetry().getMaxAttempt()) + .backOffPolicy(backOffPolicy(root.getAgent().getRetry().getBackoff())) + .complete(); + } + + private static BackOffPolicy backOffPolicy(BackOffSubProperty backOffSubProperty) { + if(backOffSubProperty == null) { + return ExponentialBackOffPolicy.DEFAULT; + } + + return new ExponentialBackOffPolicy( + backOffSubProperty.getIntervalMillis(), + backOffSubProperty.getMultiplier() + ); + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java new file mode 100644 index 00000000..cfbfe221 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.introspector.Property; +import org.yaml.snakeyaml.introspector.PropertyUtils; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RelaxedBindingUtils extends PropertyUtils { + + private static final Logger log = LoggerFactory.getLogger(RelaxedBindingUtils.class); + + @Override + public Property getProperty(final Class type, final String name) { + return super.getProperty(type, kebabToCamel(name)); + } + + private String kebabToCamel(final String kebab) { + final StringBuilder sb = new StringBuilder(); + + String[] split = kebab.split("-"); + + for(int i = 0; i < split.length; i++) { + if(i == 0) { + sb.append(split[i]); + continue; + } + sb.append(split[i].replaceFirst("^[a-z]", String.valueOf(split[i].charAt(0)).toUpperCase())); + } + + return sb.toString(); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java new file mode 100644 index 00000000..6acec047 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.AgentSubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RootYamlProperty { + + private AgentSubProperty agent; + + public RootYamlProperty() { + } + + public RootYamlProperty(final AgentSubProperty agent) { + this.agent = agent; + } + + public AgentSubProperty getAgent() { + return agent; + } + + public void setAgent(final AgentSubProperty agent) { + this.agent = agent; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java new file mode 100644 index 00000000..4e4702bb --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java @@ -0,0 +1,102 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.RetrySubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentSubProperty { + + private String name; + private String jar; + private String serverUri; + private String targetUri; + private int interval; + private RetrySubProperty retry; + + public AgentSubProperty() { + } + + public AgentSubProperty(final String name, final String jar, final String serverUri, final String targetUri, + final int interval, + final RetrySubProperty retry) { + this.name = name; + this.jar = jar; + this.serverUri = serverUri; + this.targetUri = targetUri; + this.interval = interval; + this.retry = retry; + } + + public String getServerUri() { + return serverUri; + } + + public void setServerUri(final String serverUri) { + this.serverUri = serverUri; + } + + public String getTargetUri() { + return targetUri; + } + + public void setTargetUri(final String targetUri) { + this.targetUri = targetUri; + } + + public int getInterval() { + return interval; + } + + public void setInterval(final int interval) { + this.interval = interval; + } + + public RetrySubProperty getRetry() { + return retry; + } + + public void setRetry(final RetrySubProperty retry) { + this.retry = retry; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getJar() { + return jar; + } + + public void setJar(final String jar) { + this.jar = jar; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java new file mode 100644 index 00000000..19cef295 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry; + +import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RetrySubProperty { + + private int maxAttempt; + private BackOffSubProperty backoff; + + public RetrySubProperty() { + } + + public RetrySubProperty(final int maxAttempt, final BackOffSubProperty backoff) { + this.maxAttempt = maxAttempt; + this.backoff = backoff; + } + + public int getMaxAttempt() { + return maxAttempt; + } + + public void setMaxAttempt(final int maxAttempt) { + this.maxAttempt = maxAttempt; + } + + public BackOffSubProperty getBackoff() { + return backoff; + } + + public void setBackoff(final BackOffSubProperty backoff) { + this.backoff = backoff; + } + + @Override + public String toString() { + return "RetrySubProperty{" + + "maxAttempt=" + maxAttempt + + ", backoff=" + backoff + + '}'; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java new file mode 100644 index 00000000..2bdb26d6 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class BackOffSubProperty { + + private long intervalMillis; + private int multiplier; + + public BackOffSubProperty() { + } + + public BackOffSubProperty(final long intervalMillis, final int multiplier) { + this.intervalMillis = intervalMillis; + this.multiplier = multiplier; + } + + public long getIntervalMillis() { + return intervalMillis; + } + + public void setIntervalMillis(final long intervalMillis) { + this.intervalMillis = intervalMillis; + } + + public int getMultiplier() { + return multiplier; + } + + public void setMultiplier(final int multiplier) { + this.multiplier = multiplier; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java new file mode 100644 index 00000000..1765afed --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.annotation; + +/** + * The {@code AnnotationPath} enum defines commonly used annotation paths + * for Spring-based applications, which are used for bytecode manipulation + * with ByteBuddy. + * + * @author yungwang-o + * @version 1.0.0 + */ +public enum AnnotationPath { + + TRANSACTIONAL("org.springframework.transaction.annotation.Transactional"), + SERVICE("org.springframework.stereotype.Service"), + REPOSITORY("org.springframework.stereotype.Repository"), + REST_CONTROLLER("org.springframework.web.bind.annotation.RestController"), + CONTROLLER("org.springframework.stereotype.Controller"), + ; + + private final String path; + + AnnotationPath(final String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + public static boolean filter(final String path) { + return path.equals("join") || path.equals("wait") || path.equals("notify") || path.equals("notifyAll") || + path.equals("hashcode") || path.equals("equals") || path.equals("toString"); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java new file mode 100644 index 00000000..bb93bd03 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class BootState { + + private static final Boolean STATE_NONE = false; + private static final Boolean STATE_STARTED = true; + + private final AtomicBoolean state = new AtomicBoolean(STATE_NONE); + + boolean getState() { + return state.get(); + } + + public boolean start() { + return state.compareAndSet(STATE_NONE, STATE_STARTED); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java new file mode 100644 index 00000000..b1d51e3f --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.classloading; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class THAgentClassLoader extends ClassLoader { + + public THAgentClassLoader(final ClassLoader parent) { + super(parent); + } + + @Override + protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + Class clazz = findLoadedClass(name); + + if(clazz == null) { + try { + clazz = findClass(name); + } catch (ClassNotFoundException ignored) { + } + + if(clazz == null) { + clazz = getParent().loadClass(name); + } + } + if(resolve) { + resolveClass(clazz); + } + return clazz; + } + } + + +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java new file mode 100644 index 00000000..452dbef3 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.instrument.locator; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import org.traffichunter.javaagent.bootstrap.BootstrapMain; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentLocator { + + public static File getAgentJarFile() throws URISyntaxException { + + ProtectionDomain protectionDomain = BootstrapMain.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + if(codeSource == null) { + throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain)); + } + + URL location = codeSource.getLocation(); + if(location == null) { + throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource)); + } + + final File agentJar = new File(location.toURI()); + if(agentJar.getName().endsWith(".jar")) { + throw new IllegalStateException("Agent is not a jar file: " + agentJar); + } + + return agentJar.getAbsoluteFile(); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java new file mode 100644 index 00000000..7fd7ea42 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.lifecycle; + +import java.time.Duration; +import java.time.Instant; + +/** + * The {@code LifeCycle} abstract class defines a common structure for managing + * the lifecycle of an object, including its start time, end time, and uptime. + * + *

Features:

+ *
    + *
  • Automatically initializes the start time when an instance is created.
  • + *
  • Provides abstract methods for retrieving the start time, end time, and uptime.
  • + *
  • Allows subclasses to implement specific behaviors for managing lifecycle information.
  • + *
+ * + *

Usage:

+ *
{@code
+ * public class TaskLifeCycle extends LifeCycle {
+ *
+ *     @Override
+ *     public Instant getStartTime() {
+ *         return startTime;
+ *     }
+ *
+ *     @Override
+ *     public Instant getEndTime() {
+ *         if (endTime == null) {
+ *             endTime = Instant.now();
+ *         }
+ *         return endTime;
+ *     }
+ *
+ *     @Override
+ *     public Duration getUpTime() {
+ *         return Duration.between(getStartTime(), getEndTime());
+ *     }
+ * }
+ *
+ * TaskLifeCycle task = new TaskLifeCycle();
+ * System.out.println("Uptime: " + task.getUpTime().toSeconds() + " seconds");
+ * }
+ * + * @see Instant + * @see Duration + * + * @author yungwang-o + * @version 1.0.0 +*/ +public abstract class LifeCycle { + + protected final Instant startTime; + + protected Instant endTime; + + protected LifeCycle() { + this.startTime = Instant.now(); + } + + public abstract Instant getStartTime(); + + public abstract Instant getEndTime(); + + public abstract Double getUpTime(); +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java new file mode 100644 index 00000000..3834e172 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java @@ -0,0 +1,122 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import org.traffichunter.javaagent.bootstrap.engine.property.child.FaultTolerantTrafficHunterAgent; + +/** + *

+ * The {@code TrafficHunterAgent} class is responsible for holding and managing configuration + * details required to set up and execute traffic monitoring or management tasks. + * This class provides a fluent API for constructing and customizing the configuration + * properties, such as scheduling intervals, target URIs, and resource locations. + *

+ * + *

Example Usage:

+ *
{@code
+ * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
+ *     .name("MyAgent")
+ *     .targetUri("localhost:8888")
+ *     .locationJar("/path/to/agent.jar")
+ *     .scheduleInterval(10)
+ *     .scheduleTimeUnit(TimeUnit.MINUTES)
+ *     .complete();
+ * }
+ * + *

Extensibility:

+ *
    + *
  • The class can be extended with custom behavior, as seen in {@link FaultTolerantTrafficHunterAgent}.
  • + *
+ * + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgent { + + private static final Logger log = Logger.getLogger(TrafficHunterAgent.class.getName()); + + protected int scheduleInterval; + + protected String targetUri; + + protected String jar; + + protected final String uri; + + protected TimeUnit timeUnit; + + protected String name; + + protected TrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { + this.name = trafficHunterAgent.name; + this.scheduleInterval = trafficHunterAgent.scheduleInterval; + this.targetUri = trafficHunterAgent.targetUri; + this.uri = trafficHunterAgent.uri; + this.timeUnit = trafficHunterAgent.timeUnit; + this.jar = trafficHunterAgent.jar; + } + + protected TrafficHunterAgent(final String uri) { + this.uri = uri; + } + + public static TrafficHunterAgent connect(final String serverUrl) { + return new TrafficHunterAgent(serverUrl); + } + + public FaultTolerantTrafficHunterAgent faultTolerant() { + return new FaultTolerantTrafficHunterAgent(this); + } + + public TrafficHunterAgent name(final String name) { + this.name = name; + return this; + } + + public TrafficHunterAgent targetUri(final String targetUri) { + this.targetUri = targetUri; + return this; + } + + public TrafficHunterAgent locationJar(final String jar) { + this.jar = jar; + return this; + } + + public TrafficHunterAgent scheduleInterval(final int scheduleInterval) { + this.scheduleInterval = scheduleInterval; + return this; + } + + public TrafficHunterAgent scheduleTimeUnit(final TimeUnit timeUnit) { + this.timeUnit = timeUnit; + return this; + } + + public TrafficHunterAgentProperty complete() { + return new TrafficHunterAgentProperty(name, targetUri, jar, scheduleInterval, uri, timeUnit); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java new file mode 100644 index 00000000..130460ae --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property; + +import java.util.concurrent.TimeUnit; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TrafficHunterAgentProperty( + String name, + String targetUri, + String jar, + int scheduleInterval, + String serverUri, + TimeUnit timeUnit, + int maxAttempt, + BackOffPolicy backOffPolicy +) { + + public TrafficHunterAgentProperty(final String name, + final String targetUri, + final String jar, + final int scheduleInterval, + final String serverUri, + final TimeUnit timeUnit, + final int maxAttempt, + final BackOffPolicy backOffPolicy) { + + this.name = name; + this.targetUri = targetUri; + this.jar = jar; + this.scheduleInterval = scheduleInterval; + this.serverUri = serverUri; + this.timeUnit = timeUnit; + this.maxAttempt = maxAttempt; + this.backOffPolicy = backOffPolicy; + } + + public TrafficHunterAgentProperty(final String name, + final String targetUri, + final String jar, + final int scheduleInterval, + final String serverUri, + final TimeUnit timeUnit) { + + this(name, targetUri, jar, scheduleInterval, serverUri, timeUnit, 0, null); + } + + @Override + public String toString() { + return "TrafficHunterAgentProperty{" + + "name='" + name + '\'' + + ", targetUri='" + targetUri + '\'' + + ", jar=" + jar + + ", scheduleInterval=" + scheduleInterval + + ", serverUri=" + serverUri + + ", timeUnit=" + timeUnit + + ", maxAttempt=" + maxAttempt + + ", backOffPolicy=" + backOffPolicy.toString() + + '}'; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java new file mode 100644 index 00000000..d2c8afc8 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.property.child; + +import java.util.logging.Logger; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * The {@code FaultTolerantTrafficHunterAgent} class extends the functionality of + * {@link TrafficHunterAgent} by adding fault-tolerant capabilities, such as retry logic + * and back-off policies. This class is designed to handle scenarios where failures + * may occur and ensures that the agent can recover gracefully. + * + *

Example Usage:

+ *
{@code
+ * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
+ *     .name("ResilientAgent")
+ *     .targetUri("localhost:8888")
+ *     .locationJar("/path/to/agent.jar")
+ *     .scheduleInterval(10)
+ *     .scheduleTimeUnit(TimeUnit.MINUTES)
+ *     .faultTolerant()
+ *     .retry(5) // Set maximum retry attempts to 5
+ *     .backOffPolicy(new ExponentialBackOffPolicy()) // Use exponential back-off policy
+ *     .complete();
+ * }
+ * + * @see TrafficHunterAgent + * @see BackOffPolicy + * @author yungwang-o + * @version 1.0.0 + */ +public class FaultTolerantTrafficHunterAgent extends TrafficHunterAgent { + + private static final Logger log = Logger.getLogger(FaultTolerantTrafficHunterAgent.class.getName()); + private BackOffPolicy backOffPolicy; + private int maxAttempt; + + public FaultTolerantTrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { + super(trafficHunterAgent); + } + + public FaultTolerantTrafficHunterAgent retry(final int maxAttempt) { + this.maxAttempt = maxAttempt; + return this; + } + + public FaultTolerantTrafficHunterAgent backOffPolicy(final BackOffPolicy backOffPolicy) { + this.backOffPolicy = backOffPolicy; + return this; + } + + @Override + public TrafficHunterAgentProperty complete() { + return new TrafficHunterAgentProperty( + this.name, + this.targetUri, + this.jar, + this.scheduleInterval, + this.uri, + this.timeUnit, + maxAttempt, + backOffPolicy + ); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java new file mode 100644 index 00000000..40f9ccd3 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender; + +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; + +/** + * The {@code MetricSender} interface defines the contract for sending metrics. + * Implementations of this interface are responsible for serializing and sending + * specific types of metrics using a given communication client. + * + * @see AgentTransactionMetricSender + * @see AgentSystemMetricSender + */ +public interface MetricSender { + + void toSend(AgentMetadata metadata); +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java new file mode 100644 index 00000000..45a0b07a --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java @@ -0,0 +1,198 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.manager; + +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; +import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.commons.util.AgentUtil; +import org.traffichunter.javaagent.retry.RetryHelper; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.metadata.Metadata; + +/** + * The {@code MetricSendSessionManager} class manages the session for sending + * transaction and system metrics from the TrafficHunter Agent to a server. + * It handles WebSocket connections, retries, scheduling, and lifecycle management + * of the metric-sending operations. + * + *

Purpose:

+ *
    + *
  • Manages WebSocket connections for real-time metric transmission.
  • + *
  • Schedules periodic system metric transmissions using a {@link ScheduledExecutorService}.
  • + *
  • Retries connection attempts in case of failures with a configurable back-off policy.
  • + *
  • Provides lifecycle management for starting and stopping the session.
  • + *
+ * + *

Key Features:

+ *
    + *
  • {@code run()} - Starts the metric sending session, ensuring retries and scheduling are properly configured.
  • + *
  • {@code close()} - Safely shuts down the session, releasing all resources.
  • + *
  • Integrates with {@link Retry} to handle WebSocket reconnections in case of failures.
  • + *
+ * + *

Thread Management:

+ *
    + *
  • Uses a {@link ScheduledExecutorService} for scheduling periodic system metrics.
  • + *
  • Uses a {@link ExecutorService} for running transaction metric transmissions.
  • + *
  • Both executors are safely shut down during the {@code close()} method.
  • + *
+ * + *

Limitations:

+ *
    + *
  • If the agent's context status is already {@code RUNNING}, the session cannot be restarted without stopping it first.
  • + *
  • Retries only handle specific exceptions, as configured in the {@link RetryHelper}.
  • + *
+ * + * @see Retry + * @see ScheduledExecutorService + * @see MetricWebSocketClient + * @see TrafficHunterAgentProperty + * @see AgentExecutableContext + * @see AgentMetadata + * @see RetryHelper + * @author yungwang-o + * @version 1.0.0 + */ +public class MetricSendSessionManager { + + private static final Logger log = LoggerFactory.getLogger(MetricSendSessionManager.class); + + private final TrafficHunterAgentProperty property; + + private final AgentTransactionMetricSender transactionMetricSender; + + private final AgentSystemMetricSender systemMetricSender; + + private final ScheduledExecutorService schedule; + + private final ExecutorService executor; + + private final AgentExecutableContext context; + + private final AgentMetadata metadata; + + private final MetricWebSocketClient client; + + public MetricSendSessionManager(final TrafficHunterAgentProperty property, + final AgentExecutableContext context, + final AgentMetadata metadata) { + + this.client = initializeWebsocket(property, metadata); + this.client.connect(); + this.metadata = metadata; + this.context = context; + this.property = property; + this.transactionMetricSender = new AgentTransactionMetricSender(client); + this.systemMetricSender = new AgentSystemMetricSender(client, property); + this.schedule = Executors.newSingleThreadScheduledExecutor(getThreadFactory("TransactionSystemInfoMetricSender")); + this.executor = Executors.newVirtualThreadPerTaskExecutor(); + } + + public void run() { + + if(context.isStopped()) { + log.error("MetricSendSessionManager is stopped"); + return; + } + + if(context.isRunning()) { + log.error("MetricSendSessionManager is already running"); + return; + } + + log.info("start Metric send!!"); + + RetryHelper retryHelper = RetryHelper.builder() + .backOffPolicy(property.backOffPolicy()) + .isCheck(true) + .retryName("websocket retry") + .maxAttempts(property.maxAttempt()) + .retryPredicate(throwable -> throwable instanceof IllegalStateException) + .build(); + + RetryConfig retryConfig = retryHelper.configureRetry(); + + Retry retry = Retry.of(retryHelper.getRetryName(), retryConfig); + + retry.getEventPublisher() + .onRetry(event -> { + client.reconnect(); + log.info("{} retry {} attempts...", event.getName(), event.getNumberOfRetryAttempts()); + }); + + context.setStatus(AgentStatus.RUNNING); + + executor.execute(Retry.decorateRunnable(retry, () -> transactionMetricSender.toSend(metadata))); + + schedule.scheduleWithFixedDelay(Retry.decorateRunnable(retry, () -> systemMetricSender.toSend(metadata)), + 0, + property.scheduleInterval(), + property.timeUnit() + ); + } + + public void close() { + log.info("closing MetricSendSessionManager..."); + context.setStatus(AgentStatus.EXIT); + client.close(); + executor.shutdown(); + schedule.shutdown(); + } + + private static MetricWebSocketClient initializeWebsocket(final TrafficHunterAgentProperty property, + final AgentMetadata metadata) { + return new MetricWebSocketClient( + AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), + Metadata.builder() + .agentId(metadata.agentId()) + .agentVersion(metadata.agentVersion()) + .agentName(metadata.agentName()) + .startTime(metadata.startTime()) + .status(metadata.status().get()) + .build() + ); + } + + private ThreadFactory getThreadFactory(final String threadName) { + return r -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName(threadName); + + return thread; + }; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java new file mode 100644 index 00000000..2e65b3cb --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; +import org.traffichunter.javaagent.jmx.collect.MetricCollectSupport; +import org.traffichunter.javaagent.jmx.metric.systeminfo.SystemInfo; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; + +/** + * The {@code AgentSystemMetricSender} class is responsible for sending system metrics + * to the server via a WebSocket connection. + * + *

Features:

+ *
    + *
  • Collects system metric data from the local environment.
  • + *
  • Wraps the system data with metadata and sends it in a compressed format.
  • + *
  • Uses {@link MetricCollectSupport} for metric collection.
  • + *
+ * + * @see MetricSender + * @see MetricWebSocketClient + * @see MetricCollectSupport + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentSystemMetricSender implements MetricSender { + + private static final Logger log = LoggerFactory.getLogger(AgentSystemMetricSender.class); + + private final MetricWebSocketClient client; + + private final MetricCollectSupport metricCollectSupport; + + public AgentSystemMetricSender(final MetricWebSocketClient client, + final TrafficHunterAgentProperty property) { + this.client = client; + this.metricCollectSupport = new MetricCollectSupport(property.targetUri()); + } + + @Override + public void toSend(final AgentMetadata metadata) { + final SystemInfo systemInfo = metricCollectSupport.collect(); + + final MetadataWrapper wrapper = MetadataWrapper.create(metadata, systemInfo); + + client.compressToSend(wrapper, MetricType.SYSTEM_METRIC); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java new file mode 100644 index 00000000..1a39c5c2 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; +import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; +import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; +import org.traffichunter.javaagent.trace.dto.TraceInfo; +import org.traffichunter.javaagent.trace.queue.TraceQueue; +import org.traffichunter.javaagent.websocket.MetricWebSocketClient; +import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; + +/** + *

+ * The {@code AgentTransactionMetricSender} class is responsible for sending transaction metrics + * to the server via a WebSocket connection. + *

+ * + *

Features:

+ *
    + *
  • Continuously retrieves transaction data from a synchronized queue.
  • + *
  • Wraps the transaction data with metadata and sends it in a compressed format.
  • + *
  • Relies on {@link TraceQueue} for thread-safe access to transaction data.
  • + *
+ * + * @see MetricSender + * @see MetricWebSocketClient + * @see TraceQueue + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentTransactionMetricSender implements MetricSender { + + public static final Logger log = LoggerFactory.getLogger(AgentTransactionMetricSender.class); + + private final MetricWebSocketClient client; + + public AgentTransactionMetricSender(final MetricWebSocketClient client) { + this.client = client; + } + + @Override + public void toSend(final AgentMetadata metadata) { + + while (!Thread.currentThread().isInterrupted()) { + + try { + + TraceInfo trInfo = TraceQueue.INSTANCE.poll(); + + MetadataWrapper wrapper = MetadataWrapper.create(metadata, trInfo); + + client.compressToSend(wrapper, MetricType.TRANSACTION_METRIC); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (IllegalStateException e) { + log.error("exception while sending transaction metric = {}", e.getMessage()); + throw new RuntimeException(e); + } + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java new file mode 100644 index 00000000..3b65d6cb --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.metadata; + +import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.event.listener.AgentStateEventListener; +import org.traffichunter.javaagent.event.object.AgentStateEvent; + +/** + * The {@code AgentMetadata} record represents metadata for an agent and acts as an + * {@link AgentStateEventListener} to respond to state change events. + * + *

Features:

+ *
    + *
  • Stores immutable metadata fields such as agent ID, version, name, and start time.
  • + *
  • Maintains a mutable {@link AtomicReference} for the agent's current status.
  • + *
  • Implements {@link AgentStateEventListener} to react to state change events.
  • + *
  • Overrides {@code equals} and {@code hashCode} to handle mutable status appropriately.
  • + *
+ * + * @see AgentStateEventListener + * @see AgentStateEvent + * @see AgentStatus + * @see AtomicReference + * + * @author yungwang-o + * @version 1.0.0 + */ +public record AgentMetadata( + String agentId, + String agentVersion, + String agentName, + Instant startTime, + AtomicReference status +) implements AgentStateEventListener { + + @Override + public void onEvent(final AgentStateEvent event) { + this.setStatus(event.getAfterStatus()); + } + + private void setStatus(final AgentStatus status) { + this.status.set(status); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AgentMetadata that = (AgentMetadata) o; + return Objects.equals(agentId, that.agentId) && + Objects.equals(agentVersion, that.agentVersion) && + Objects.equals(agentName, that.agentName) && + Objects.equals(startTime, that.startTime) && + Objects.equals(status.get(), that.status.get()); + } + + @Override + public int hashCode() { + return Objects.hash(agentId, agentVersion, agentName, startTime, status.get()); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java new file mode 100644 index 00000000..64b9bd93 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap.metadata; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record MetadataWrapper(AgentMetadata metadata, D data) { + + public static MetadataWrapper create(AgentMetadata metadata, D data) { + return new MetadataWrapper<>(metadata, data); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt b/java-apm-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt new file mode 100644 index 00000000..adb4b4ec --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt @@ -0,0 +1,11 @@ +_____ ___________________ ______ _____ +__ /_____________ ___ __/__ __/__(_)______ ___ /_____ __________ /_____________ +_ __/_ ___/ __ `/_ /_ __ /_ __ /_ ___/ __ __ \ / / /_ __ \ __/ _ \_ ___/ +/ /_ _ / / /_/ /_ __/ _ __/ _ / / /__ _ / / / /_/ /_ / / / /_ / __/ / +\__/ /_/ \__,_/ /_/ /_/ /_/ \___/ /_/ /_/\__,_/ /_/ /_/\__/ \___//_/ + +:: Traffic Hunter ${version} Ver :: +:: Made by ygo :: +:: Running on Java ${java.version} :: +:: JDK Information ${jdk} ${java.specification} :: +:: Started on ${time} :: \ No newline at end of file diff --git a/java-apm-agent/java-agent-commons/build.gradle b/java-apm-agent/java-agent-commons/build.gradle new file mode 100644 index 00000000..c2688fc2 --- /dev/null +++ b/java-apm-agent/java-agent-commons/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.commons' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^Purpose:

+ *
    + *
  • Stores event listeners for agent state changes.
  • + *
  • Optimized for frequent reads and infrequent writes using {@link CopyOnWriteArrayList}.
  • + *
+ * + * @see AgentStateEventListener + * @see CopyOnWriteArrayList + * + * @author yungwang-o + * @version 1.0.0 + */ +public class AgentStateEventStore { + + private final List listeners = new CopyOnWriteArrayList<>(); + + protected void addAgentStateEventListener(final AgentStateEventListener listener) { + listeners.add(listener); + } + + protected void removeAgentStateEventListener(final AgentStateEventListener listener) { + if(listeners.isEmpty()) { + throw new IllegalArgumentException("listener is empty"); + } + + listeners.remove(listener); + } + + public List getListeners() { + return listeners; + } + + protected void removeAll() { + listeners.clear(); + } + + public int listenerSize() { + return listeners.size(); + } +} diff --git a/java-apm-agent/java-agent-jmx/build.gradle b/java-apm-agent/java-agent-jmx/build.gradle new file mode 100644 index 00000000..e6a348d9 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.jmx' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-jmx/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-jmx/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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:

+ *
    + *
  • Extends {@link MetricCollector} for metric collection.
  • + *
  • Provides helper methods to query MBeans and retrieve attributes.
  • + *
  • Handles exceptions gracefully through the nested {@link MetricCollectionException} class.
  • + *
+ * + * @see MetricCollector + * @see MBeanServer + * + * @author yungwang-o + * @version 1.0.0 + */ +public abstract class AbstractMBeanMetricCollector implements MetricCollector { + + protected final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + protected Set findObjectName(final String objectName) { + try { + return mBeanServer.queryNames(new ObjectName(objectName), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + } + + protected
A getAttribute(final ObjectName name, final String attribute, final Class type) { + try { + + return type.cast(mBeanServer.getAttribute(name, attribute)); + } catch (Exception e) { + throw new MetricCollectionException("Failed to get attribute: " + attribute, e); + } + } + + public static class MetricCollectionException extends RuntimeException { + + public MetricCollectionException() { + super(); + } + + public MetricCollectionException(final String message) { + super(message); + } + + public MetricCollectionException(final String message, final Throwable cause) { + super(message, cause); + } + + public MetricCollectionException(final Throwable cause) { + super(cause); + } + + protected MetricCollectionException(final String message, final Throwable cause, + final boolean enableSuppression, + final boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollectSupport.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollectSupport.java new file mode 100644 index 00000000..1ed439a6 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollectSupport.java @@ -0,0 +1,130 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect; + +import java.io.IOException; +import java.time.Instant; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import org.traffichunter.javaagent.jmx.collect.dbcp.hikari.HikariCPMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.cpu.CpuMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.gc.GarbageCollectionMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.memory.MemoryMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.runtime.RuntimeMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.thread.ThreadMetricCollector; +import org.traffichunter.javaagent.jmx.collect.web.tomcat.TomcatMetricCollector; +import org.traffichunter.javaagent.jmx.jvm.JVMSelector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.SystemInfo; + +/** + * The {@code MetricCollectSupport} class provides functionality for collecting system metrics + * from the local JVM or a target JVM via JMX. + * + *

Features:

+ *
    + *
  • Integrates multiple metric collectors for memory, CPU, threads, garbage collection, + * runtime, Tomcat, and HikariCP metrics.
  • + *
  • Supports collecting metrics from the local JVM or a remote JVM identified by a JMX path.
  • + *
  • Combines collected metrics into a unified {@link SystemInfo} object.
  • + *
+ * + * @see SystemInfo + * @see MemoryMetricCollector + * @see CpuMetricCollector + * @see ThreadMetricCollector + * @see GarbageCollectionMetricCollector + * @see RuntimeMetricCollector + * @see TomcatMetricCollector + * @see HikariCPMetricCollector + * + * @author yungwang-o + * @version 1.0.0 + */ + +public class MetricCollectSupport { + + private static final Logger log = Logger.getLogger(MetricCollectSupport.class.getName()); + + private final MemoryMetricCollector collectorMemory; + + private final CpuMetricCollector collectorCpu; + + private final ThreadMetricCollector collectorThread; + + private final GarbageCollectionMetricCollector collectorGC; + + private final RuntimeMetricCollector collectorRuntime; + + private final TomcatMetricCollector collectorTomcat; + + private final HikariCPMetricCollector collectorHikari; + + public MetricCollectSupport(final String targetUri) { + this.collectorMemory = new MemoryMetricCollector(); + this.collectorCpu = new CpuMetricCollector(); + this.collectorThread = new ThreadMetricCollector(); + this.collectorGC = new GarbageCollectionMetricCollector(); + this.collectorRuntime = new RuntimeMetricCollector(); + this.collectorTomcat = new TomcatMetricCollector(targetUri); + this.collectorHikari = new HikariCPMetricCollector(); + } + + public SystemInfo collect(final String targetJVMPath) { + + try (final JMXConnector jmxConnector = JMXConnectorFactory.connect(JVMSelector.getVMXServiceUrl(targetJVMPath))) { + + final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection(); + + return new SystemInfo( + Instant.now(), + collectorMemory.collect(mbsc), + collectorThread.collect(mbsc), + collectorCpu.collect(mbsc), + collectorGC.collect(mbsc), + collectorRuntime.collect(mbsc), + collectorTomcat.collect(mbsc), + collectorHikari.collect(mbsc) + ); + + } catch (IOException e) { + log.severe("Failed to start local management agent : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + public SystemInfo collect() { + return new SystemInfo( + Instant.now(), + collectorMemory.collect(), + collectorThread.collect(), + collectorCpu.collect(), + collectorGC.collect(), + collectorRuntime.collect(), + collectorTomcat.collect(), + collectorHikari.collect() + ); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollector.java new file mode 100644 index 00000000..4c912e7c --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollector.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect; + +import java.util.List; +import javax.management.MBeanServerConnection; + +/** + * The {@code MetricCollector} interface defines a contract for collecting metrics. + * Implementations can collect metrics from various sources, such as the local JVM + * or an MBeanServer. + * + *

Features:

+ *
    + *
  • Provides methods for collecting a single metric or all available metrics.
  • + *
  • Includes default implementations for optional operations.
  • + *
+ * + * @param The type of metric being collected. + * + * @author yungwang-o + * @version 1.0.0 + */ +public interface MetricCollector { + + default T collect(MBeanServerConnection mbsc) { + return null; + } + + T collect(); + + default List collectAll() {return null;} +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/dbcp/hikari/HikariCPMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/dbcp/hikari/HikariCPMetricCollector.java new file mode 100644 index 00000000..be3b8921 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/dbcp/hikari/HikariCPMetricCollector.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.dbcp.hikari; + +import javax.management.ObjectName; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.dbcp.HikariDbcpInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class HikariCPMetricCollector extends AbstractMBeanMetricCollector { + + @Override + public HikariDbcpInfo collect() { + try { + + //The default name for a Hikari connection pool is "HikariPool-1" + ObjectName hikariInfo = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)"); + + int activeConnections = getAttribute(hikariInfo, "ActiveConnections", Integer.class); + int idleConnections = getAttribute(hikariInfo, "IdleConnections", Integer.class); + int totalConnections = getAttribute(hikariInfo, "TotalConnections", Integer.class); + int threadsAwaitingConnections = getAttribute(hikariInfo, "ThreadsAwaitingConnection", Integer.class); + + return new HikariDbcpInfo( + activeConnections, + idleConnections, + totalConnections, + threadsAwaitingConnections + ); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/cpu/CpuMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/cpu/CpuMetricCollector.java new file mode 100644 index 00000000..1438925a --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/cpu/CpuMetricCollector.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.systeminfo.cpu; + +import com.sun.management.OperatingSystemMXBean; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.cpu.CpuStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class CpuMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(CpuStatusInfo.class.getName()); + + @Override + public CpuStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final OperatingSystemMXBean osMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, + OperatingSystemMXBean.class + ); + + return new CpuStatusInfo( + osMXBean.getCpuLoad(), + osMXBean.getProcessCpuLoad(), + osMXBean.getAvailableProcessors() + ); + } catch (IOException e) { + log.warning("Failed to get cpu metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public CpuStatusInfo collect() { + final OperatingSystemMXBean osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + + return new CpuStatusInfo( + osMXBean.getCpuLoad(), + osMXBean.getProcessCpuLoad(), + osMXBean.getAvailableProcessors() + ); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/gc/GarbageCollectionMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/gc/GarbageCollectionMetricCollector.java new file mode 100644 index 00000000..f25027f4 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/gc/GarbageCollectionMetricCollector.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.systeminfo.gc; + +import java.io.IOException; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.collect.systeminfo.memory.MemoryMetricCollector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.gc.GarbageCollectionStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.gc.collections.GarbageCollectionTime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class GarbageCollectionMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); + + @Override + public GarbageCollectionStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final List mxBeans = ManagementFactory.getPlatformMXBeans(mbsc, + GarbageCollectorMXBean.class); + + final List garbageCollectionTimes = mxBeans + .stream() + .map(GarbageCollectionTime::new) + .toList(); + + return new GarbageCollectionStatusInfo(garbageCollectionTimes); + } catch (IOException e) { + log.warning("Failed to get GC metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public GarbageCollectionStatusInfo collect() { + final List mxBeans = ManagementFactory.getGarbageCollectorMXBeans(); + + final List garbageCollectionTimes = mxBeans + .stream() + .map(GarbageCollectionTime::new) + .toList(); + + return new GarbageCollectionStatusInfo(garbageCollectionTimes); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/memory/MemoryMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/memory/MemoryMetricCollector.java new file mode 100644 index 00000000..dad0be27 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/memory/MemoryMetricCollector.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.systeminfo.memory; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.memory.MemoryStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.memory.MemoryStatusInfo.MemoryUsage; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class MemoryMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); + + @Override + public MemoryStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.MEMORY_MXBEAN_NAME, + MemoryMXBean.class + ); + + final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); + final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); + + return new MemoryStatusInfo( + new MemoryUsage( + heapMemoryUsage.getInit(), + heapMemoryUsage.getUsed(), + heapMemoryUsage.getCommitted(), + heapMemoryUsage.getMax() + ), + new MemoryUsage( + nonHeapMemoryUsage.getInit(), + nonHeapMemoryUsage.getUsed(), + nonHeapMemoryUsage.getCommitted(), + nonHeapMemoryUsage.getMax() + ) + ); + } catch (IOException e) { + log.warning("Failed to get heap memory metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public MemoryStatusInfo collect() { + final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + + final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); + final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); + + return new MemoryStatusInfo( + new MemoryUsage( + heapMemoryUsage.getInit(), + heapMemoryUsage.getUsed(), + heapMemoryUsage.getCommitted(), + heapMemoryUsage.getMax() + ), + new MemoryUsage( + nonHeapMemoryUsage.getInit(), + nonHeapMemoryUsage.getUsed(), + nonHeapMemoryUsage.getCommitted(), + nonHeapMemoryUsage.getMax() + ) + ); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/runtime/RuntimeMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/runtime/RuntimeMetricCollector.java new file mode 100644 index 00000000..6230eec2 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/runtime/RuntimeMetricCollector.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.systeminfo.runtime; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.runtime.RuntimeStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class RuntimeMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(RuntimeStatusInfo.class.getName()); + + @Override + public RuntimeStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.RUNTIME_MXBEAN_NAME, + RuntimeMXBean.class + ); + + return new RuntimeStatusInfo( + runtimeMXBean.getStartTime(), + runtimeMXBean.getUptime(), + runtimeMXBean.getVmName(), + runtimeMXBean.getVmVersion() + ); + } catch (IOException e) { + log.warning("Failed to get runtime metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public RuntimeStatusInfo collect() { + final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + + return new RuntimeStatusInfo( + runtimeMXBean.getStartTime(), + runtimeMXBean.getUptime(), + runtimeMXBean.getVmName(), + runtimeMXBean.getVmVersion() + ); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/thread/ThreadMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/thread/ThreadMetricCollector.java new file mode 100644 index 00000000..d65b8091 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/systeminfo/thread/ThreadMetricCollector.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.systeminfo.thread; + +import com.sun.management.ThreadMXBean; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; +import javax.management.MBeanServerConnection; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.systeminfo.thread.ThreadStatusInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class ThreadMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(ThreadMetricCollector.class.getName()); + + @Override + public ThreadStatusInfo collect(final MBeanServerConnection mbsc) { + try { + final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( + mbsc, + ManagementFactory.THREAD_MXBEAN_NAME, + ThreadMXBean.class + ); + + return new ThreadStatusInfo( + threadMXBean.getThreadCount(), + threadMXBean.getPeakThreadCount(), + threadMXBean.getTotalStartedThreadCount() + ); + } catch (IOException e) { + log.warning("Failed to get thread metric: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public ThreadStatusInfo collect() { + final ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); + + return new ThreadStatusInfo( + threadMXBean.getThreadCount(), + threadMXBean.getPeakThreadCount(), + threadMXBean.getTotalStartedThreadCount() + ); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/web/tomcat/TomcatMetricCollector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/web/tomcat/TomcatMetricCollector.java new file mode 100644 index 00000000..a213bdd2 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/web/tomcat/TomcatMetricCollector.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.collect.web.tomcat; + +import java.util.logging.Logger; +import javax.management.ObjectName; +import org.traffichunter.javaagent.jmx.collect.AbstractMBeanMetricCollector; +import org.traffichunter.javaagent.jmx.metric.web.tomcat.TomcatWebServerInfo; +import org.traffichunter.javaagent.jmx.metric.web.tomcat.request.TomcatRequestInfo; +import org.traffichunter.javaagent.jmx.metric.web.tomcat.thread.TomcatThreadPoolInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class TomcatMetricCollector extends AbstractMBeanMetricCollector { + + private static final Logger log = Logger.getLogger(TomcatMetricCollector.class.getName()); + + private final String targetUri; + + public TomcatMetricCollector(final String targetUri) { + this.targetUri = targetUri; + } + + @Override + public TomcatWebServerInfo collect() { + + try { + ObjectName threadPoolMbean = new ObjectName("Tomcat:type=ThreadPool,name=" + getPort()); + ObjectName requestMBean = new ObjectName("Tomcat:type=GlobalRequestProcessor,name=" + getPort()); + + int maxThreads = getAttribute(threadPoolMbean, "maxThreads", Integer.class); + int currentThreads = getAttribute(threadPoolMbean, "currentThreadCount", Integer.class); + int currentThreadsBusy = getAttribute(threadPoolMbean, "currentThreadsBusy", Integer.class); + + int requestCount = getAttribute(requestMBean, "requestCount", Integer.class); + long bytesReceived = getAttribute(requestMBean, "bytesReceived", Long.class); + long bytesSent = getAttribute(requestMBean, "bytesSent", Long.class); + long processingTime = getAttribute(requestMBean, "processingTime", Long.class); + int errorCount = getAttribute(requestMBean, "errorCount", Integer.class); + + return new TomcatWebServerInfo( + new TomcatThreadPoolInfo(maxThreads, currentThreads, currentThreadsBusy), + new TomcatRequestInfo(requestCount, bytesReceived, bytesSent, processingTime, errorCount) + ); + } catch (Exception e) { + log.severe("Failed to collect metrics : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * target uri -> localhost:8080 + *
+ * translation -> localhost:8080 -> http-nio-8080 + * @return port + */ + private String getPort() { + final String port = targetUri.split(":")[1]; + return String.format("\"http-nio-%s\"", port); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/jvm/JVMSelector.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/jvm/JVMSelector.java new file mode 100644 index 00000000..daa392c8 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/jvm/JVMSelector.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.jvm; + +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; +import java.io.IOException; +import java.util.Objects; +import java.util.logging.Logger; +import javax.management.remote.JMXServiceURL; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class JVMSelector { + + private static final Logger log = Logger.getLogger(JVMSelector.class.getName()); + + public static JMXServiceURL getVMXServiceUrl(final String targetJVMPath) { + + try { + final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); + + final VirtualMachine vm = VirtualMachine.attach(vmDescriptor.id().trim()); + + final String jmxUrl = vm.startLocalManagementAgent(); + + return new JMXServiceURL(jmxUrl); + } catch (AttachNotSupportedException | IOException e) { + log.warning("Not found jvm service url : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + public static VirtualMachine getVM(final String targetJVMPath) { + + try { + final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); + + return VirtualMachine.attach(vmDescriptor.id().trim()); + + } catch (AttachNotSupportedException | IOException e) { + log.warning("Not found jvm service url : " + e.getMessage()); + throw new RuntimeException(e); + } + } + + private static VirtualMachineDescriptor getVirtualMachineDescriptor(final String targetJVMPath) { + + return VirtualMachine.list().stream() + .filter(vm -> Objects.equals(vm.displayName(), targetJVMPath)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No virtual machine found")); + } + + public static String displayName(final int selectNumber) { + return VirtualMachine.list().get(selectNumber - 1).displayName(); + } + + public static String JvmId(final int selectNumber) { + return VirtualMachine.list().get(selectNumber - 1).id(); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/dbcp/HikariDbcpInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/dbcp/HikariDbcpInfo.java new file mode 100644 index 00000000..b5f66320 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/dbcp/HikariDbcpInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.dbcp; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record HikariDbcpInfo( + // Connection Status + int activeConnections, // Number of active connections currently in use + int idleConnections, // Number of idle connections in the pool + int totalConnections, // Total number of connections in the pool + int threadsAwaitingConnection // Number of threads waiting for a connection +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/SystemInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/SystemInfo.java new file mode 100644 index 00000000..35ac25f2 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/SystemInfo.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo; + +import java.time.Instant; +import org.traffichunter.javaagent.jmx.metric.dbcp.HikariDbcpInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.cpu.CpuStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.gc.GarbageCollectionStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.memory.MemoryStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.runtime.RuntimeStatusInfo; +import org.traffichunter.javaagent.jmx.metric.systeminfo.thread.ThreadStatusInfo; +import org.traffichunter.javaagent.jmx.metric.web.tomcat.TomcatWebServerInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record SystemInfo( + Instant time, + MemoryStatusInfo memoryStatusInfo, + ThreadStatusInfo threadStatusInfo, + CpuStatusInfo cpuStatusInfo, + GarbageCollectionStatusInfo garbageCollectionStatusInfo, + RuntimeStatusInfo runtimeStatusInfo, + TomcatWebServerInfo tomcatWebServerInfo, + HikariDbcpInfo hikariDbcpInfo +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/cpu/CpuStatusInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/cpu/CpuStatusInfo.java new file mode 100644 index 00000000..5219eed8 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/cpu/CpuStatusInfo.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.cpu; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record CpuStatusInfo(double systemCpuLoad, double processCpuLoad, long availableProcessors) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/GarbageCollectionStatusInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/GarbageCollectionStatusInfo.java new file mode 100644 index 00000000..cd3a1c42 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/GarbageCollectionStatusInfo.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.gc; + +import java.util.List; +import org.traffichunter.javaagent.jmx.metric.systeminfo.gc.collections.GarbageCollectionTime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record GarbageCollectionStatusInfo(List garbageCollectionTimes) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/collections/GarbageCollectionTime.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/collections/GarbageCollectionTime.java new file mode 100644 index 00000000..1b85a180 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/gc/collections/GarbageCollectionTime.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.gc.collections; + +import java.lang.management.GarbageCollectorMXBean; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record GarbageCollectionTime( + long getCollectionCount, + long getCollectionTime +) { + + public GarbageCollectionTime(final GarbageCollectorMXBean mxBean) { + this(mxBean.getCollectionCount(), mxBean.getCollectionTime()); + } +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/memory/MemoryStatusInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/memory/MemoryStatusInfo.java new file mode 100644 index 00000000..0bcecc04 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/memory/MemoryStatusInfo.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.memory; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record MemoryStatusInfo(MemoryUsage heapMemoryUsage, MemoryUsage nonHeapMemoryUsage) { + + public record MemoryUsage(long init, long used, long committed, long max) {} +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/runtime/RuntimeStatusInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/runtime/RuntimeStatusInfo.java new file mode 100644 index 00000000..80f58e18 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/runtime/RuntimeStatusInfo.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.runtime; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record RuntimeStatusInfo( + long getStartTime, + long getUpTime, + String getVmName, + String getVmVersion +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/thread/ThreadStatusInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/thread/ThreadStatusInfo.java new file mode 100644 index 00000000..57d2ed7f --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/systeminfo/thread/ThreadStatusInfo.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.systeminfo.thread; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record ThreadStatusInfo( + int threadCount, + int getPeekThreadCount, + long getTotalStartThreadCount +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/TomcatWebServerInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/TomcatWebServerInfo.java new file mode 100644 index 00000000..1951bf82 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/TomcatWebServerInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.web.tomcat; + +import org.traffichunter.javaagent.jmx.metric.web.tomcat.request.TomcatRequestInfo; +import org.traffichunter.javaagent.jmx.metric.web.tomcat.thread.TomcatThreadPoolInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatWebServerInfo( + TomcatThreadPoolInfo tomcatThreadPoolInfo, + TomcatRequestInfo tomcatRequestInfo +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/request/TomcatRequestInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/request/TomcatRequestInfo.java new file mode 100644 index 00000000..66820f24 --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/request/TomcatRequestInfo.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.web.tomcat.request; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatRequestInfo( + long requestCount, + long bytesReceived, + long bytesSent, + long processingTime, + long errorCount +) { +} diff --git a/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/thread/TomcatThreadPoolInfo.java b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/thread/TomcatThreadPoolInfo.java new file mode 100644 index 00000000..bdc6ab8f --- /dev/null +++ b/java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/metric/web/tomcat/thread/TomcatThreadPoolInfo.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.jmx.metric.web.tomcat.thread; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public record TomcatThreadPoolInfo( + int maxThreads, + int currentThreads, + int currentThreadsBusy +) { +} diff --git a/java-apm-agent/java-agent-retry/build.gradle b/java-apm-agent/java-agent-retry/build.gradle new file mode 100644 index 00000000..c31598f4 --- /dev/null +++ b/java-apm-agent/java-agent-retry/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.retry' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^Features:

+ *
    + *
  • Provides a fluent {@code Builder} for configuring retry parameters.
  • + *
  • Supports customizable backoff policies via {@link BackOffPolicy}.
  • + *
  • Configures exception-based retry logic with a {@link Predicate}.
  • + *
  • Generates a {@link RetryConfig} compatible with retry libraries.
  • + *
+ * + *

Example:

+ *
{@code
+ * RetryHelper retryHelper = RetryHelper.builder()
+ *     .backOffPolicy(new BackOffPolicy(1000, 2))
+ *     .maxAttempts(5)
+ *     .retryPredicate(e -> e instanceof IllegalStateException)
+ *     .retryName("SampleRetry")
+ *     .isCheck(true)
+ *     .build();
+ *
+ * RetryConfig retryConfig = retryHelper.configureRetry();
+ * }
+ * + *

Thread Safety:

+ *
    + *
  • This class is immutable and thread-safe once built.
  • + *
+ * + * @see RetryConfig + * @see BackOffPolicy + * + * @author yungwang-o + * @version 1.0.0 + */ +public class RetryHelper { + + private static final Logger log = Logger.getLogger(RetryHelper.class.getName()); + + private final BackOffPolicy backOffPolicy; + + private final int maxAttempts; + + private final Predicate retryPredicate; + + private final String retryName; + + private final boolean isCheck; + + private RetryHelper(final Builder builder) { + this.backOffPolicy = builder.backOffPolicy; + this.maxAttempts = builder.maxAttempts; + this.retryPredicate = builder.retryPredicate; + this.retryName = builder.retryName; + this.isCheck = builder.isCheck; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private BackOffPolicy backOffPolicy; + + private int maxAttempts; + + private Predicate retryPredicate; + + private String retryName; + + private boolean isCheck; + + public Builder() { + } + + public Builder backOffPolicy(final BackOffPolicy backOffPolicy) { + this.backOffPolicy = backOffPolicy; + return this; + } + + public Builder maxAttempts(final int maxAttempts) { + this.maxAttempts = maxAttempts; + return this; + } + + public Builder retryPredicate(final Predicate retryPredicate) { + this.retryPredicate = retryPredicate; + return this; + } + + public Builder retryName(final String retryName) { + this.retryName = retryName; + return this; + } + + public Builder isCheck(final boolean isCheck) { + this.isCheck = isCheck; + return this; + } + + public RetryHelper build() { + return new RetryHelper(this); + } + } + + public BackOffPolicy getBackOffPolicy() { + return backOffPolicy; + } + + public int getMaxAttempts() { + return maxAttempts; + } + + public Predicate getRetryPredicate() { + return retryPredicate; + } + + public String getRetryName() { + return retryName; + } + + public boolean isCheck() { + return isCheck; + } + + public RetryConfig configureRetry() { + return RetryConfig.custom() + .maxAttempts(maxAttempts) + .retryOnException(retryPredicate) + .failAfterMaxAttempts(isCheck) + .intervalFunction(IntervalFunction.ofExponentialRandomBackoff( + backOffPolicy.getIntervalMillis(), + backOffPolicy.getMultiplier() + )).build(); + } +} diff --git a/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java new file mode 100644 index 00000000..14916566 --- /dev/null +++ b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff; + +/** + * The {@code BackOffPolicy} class serves as the base class for defining backoff strategies + * used in retry mechanisms. It provides common properties such as the interval and multiplier, + * which can be extended for specific backoff behaviors. + * + *

Subclasses:

+ *
    + *
  • {@link ExponentialBackOffPolicy}: Implements exponential backoff strategy.
  • + *
  • {@link FixedBackOffPolicy}: Implements fixed interval backoff strategy.
  • + *
+ * + * @author yungwang-o + * @version 1.0.0 + */ +public class BackOffPolicy { + + private final long intervalMillis; + private final int multiplier; + + public BackOffPolicy(final long intervalMillis, final int multiplier) { + this.intervalMillis = intervalMillis; + this.multiplier = multiplier; + } + + public long getIntervalMillis() { + return intervalMillis; + } + + public int getMultiplier() { + return multiplier; + } + + @Override + public String toString() { + return "BackOffPolicy{" + + "intervalMillis=" + intervalMillis + + ", multiplier=" + multiplier + + '}'; + } +} diff --git a/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java new file mode 100644 index 00000000..586bbb00 --- /dev/null +++ b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff.policy; + +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class ExponentialBackOffPolicy extends BackOffPolicy { + + public static final ExponentialBackOffPolicy DEFAULT = new ExponentialBackOffPolicy(1000, 2); + + public ExponentialBackOffPolicy(final long intervalMillis, final int multiplier) { + super(intervalMillis, multiplier); + } +} diff --git a/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java new file mode 100644 index 00000000..8c3597d4 --- /dev/null +++ b/java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.retry.backoff.policy; + +import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class FixedBackOffPolicy extends BackOffPolicy { + + public static final FixedBackOffPolicy DEFAULT = new FixedBackOffPolicy(1000); + + public FixedBackOffPolicy(final long intervalMillis) { + super(intervalMillis, 1); + } +} diff --git a/java-apm-agent/java-agent-trace/build.gradle b/java-apm-agent/java-agent-trace/build.gradle new file mode 100644 index 00000000..abf614a7 --- /dev/null +++ b/java-apm-agent/java-agent-trace/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.trace' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-apm-agent:java-agent-commons') + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 spans) { + + if(isShutdown) { + return CompletableResultCode.ofFailure(); + } + + try { + spans.stream() + .map(TraceInfo::translate) + .forEach(TraceQueue.INSTANCE::add); + + return CompletableResultCode.ofSuccess(); + } catch (RuntimeException e) { + return CompletableResultCode.ofFailure(); + } + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + + isShutdown = true; + + try { + return CompletableResultCode.ofSuccess(); + } catch (Exception e) { + return CompletableResultCode.ofFailure(); + } + } +} diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java new file mode 100644 index 00000000..3aa95a7b --- /dev/null +++ b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.manager; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.IdGenerator; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.util.Objects; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public class TraceManager { + + private static final String INSTRUMENTATION_SCOPE_NAME = "instrument-transaction-scope"; + + private static volatile SdkTracerProvider provider; + + private static volatile OpenTelemetrySdk openTelemetrySdk; + + public static Tracer configure(final SpanExporter exporter) { + + provider = SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .setIdGenerator(IdGenerator.random()) + .setSampler(Sampler.alwaysOn()) + .build(); + + openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(provider) + .build(); + + return openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME); + } + + public static void close() { + if (Objects.nonNull(provider) && Objects.nonNull(openTelemetrySdk)) { + provider.close(); + openTelemetrySdk.close(); + } + } + + public record SpanScope(Span span, Scope scope) { } +} diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java new file mode 100644 index 00000000..3dbbf577 --- /dev/null +++ b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.trace.queue; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import org.traffichunter.javaagent.trace.dto.TraceInfo; + +/** + * @author yungwang-o + * @version 1.0.0 + */ +public enum TraceQueue { + + INSTANCE, + ; + + private final BlockingQueue syncQ = new LinkedBlockingQueue<>(100); + + /** + * this method is non-blocking + * @return success : true, fail : false + */ + public boolean add(final TraceInfo trInfo) { + return syncQ.offer(trInfo); + } + + /** + * this method is blocking + */ + public TraceInfo poll() throws InterruptedException { + return syncQ.take(); + } + + public void removeAll() { + syncQ.clear(); + } + + public int size() { + return syncQ.size(); + } +} diff --git a/java-apm-agent/java-agent-websocket/build.gradle b/java-apm-agent/java-agent-websocket/build.gradle new file mode 100644 index 00000000..528d8fd4 --- /dev/null +++ b/java-apm-agent/java-agent-websocket/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.websocket' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-apm-agent:java-agent-commons') + + implementation 'org.java-websocket:Java-WebSocket:1.5.7' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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:

+ *
    + *
  • Automatically sends metadata when the connection is opened.
  • + *
  • Supports sending metrics as JSON or compressed binary data.
  • + *
  • Provides utility methods to check and manage WebSocket connection state.
  • + *
+ * + * @see WebSocketClient + * @see SerializationByteArrayConverter + * @see Metadata + * @see MetricType + * + * @author yungwang-o + * @version 1.0.0 + */ +public class MetricWebSocketClient extends WebSocketClient { + + private static final Logger log = LoggerFactory.getLogger(MetricWebSocketClient.class); + + private final SerializationByteArrayConverter converter; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final Metadata metadata; + + public MetricWebSocketClient(final URI serverUri, final Metadata metadata) { + super(serverUri); + this.metadata = metadata; + this.objectMapper + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .registerModule(new JavaTimeModule()); + this.converter = new SerializationByteArrayConverter(objectMapper); + } + + @Override + public void onOpen(final ServerHandshake serverHandshake) { + log.info("websocket client opened"); + this.toSend(metadata); + } + + @Override + public void onMessage(final String s) { + log.info("websocket client received = {}", s); + } + + + @Override + public void onClose(final int i, final String s, final boolean b) { + log.info("websocket client closed"); + } + + @Override + public void onError(final Exception e) { + log.error("websocket client error = {}", e.getMessage()); + } + + public boolean isConnected() { + if(isOpen()) { + return true; + } + + try { + return super.connectBlocking(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return false; + } + + public boolean isConnected(final long timeOut, final TimeUnit timeUnit) { + if(isOpen()) { + return true; + } + + try { + return super.connectBlocking(timeOut, timeUnit); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + return false; + } + + public void toSend(final M metric) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + try { + String s = objectMapper.writeValueAsString(metric); + this.send(s); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public void compressToSend(final M metric, final MetricType metricType) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + byte[] transform = converter.transform(metric, metricType); + + this.send(transform); + } + + public void toSend(final List metrics) { + if(!isOpen()) { + throw new IllegalStateException("WebSocket client is closed"); + } + + try { + this.send(objectMapper.writeValueAsString(metrics)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java b/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java new file mode 100644 index 00000000..33f2304a --- /dev/null +++ b/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java @@ -0,0 +1,105 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.websocket.converter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * The {@code SerializationByteArrayConverter} class provides utility methods for + * serializing objects into compressed byte arrays and deserializing compressed byte arrays + * back into objects. This is useful for efficient data transmission or storage. + * + *

Key Features:

+ *
    + *
  • Serializes objects to compressed byte arrays with a metric type identifier.
  • + *
  • Deserializes compressed byte arrays back into objects of specified types.
  • + *
  • Supports gzip compression for optimized size reduction.
  • + *
+ * + *

Usage:

+ *
    + *
  • {@code transform(Object, MetricType)}: Converts an object to a compressed byte array.
  • + *
  • {@code inverseTransform(byte[], TypeReference)}: Converts a compressed byte array back into an object.
  • + *
+ * + * @see ObjectMapper + * @see GZIPOutputStream + * @see GZIPInputStream + * @author yungwang-o + * @version 1.0.0 + */ +public class SerializationByteArrayConverter { + + private final ObjectMapper objectMapper; + + public SerializationByteArrayConverter(final ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public byte[] transform(final Object obj, final MetricType metricType) { + byte[] data = serialize(obj); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ + + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) { + gzipOutputStream.write(data); + } + + byte[] compressByteArray = baos.toByteArray(); + byte[] result = new byte[baos.toByteArray().length + 1]; + + result[0] = metricType.value; + + System.arraycopy(compressByteArray, 0, result, 1, compressByteArray.length); + + return result; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private byte[] serialize(final Object object) { + try { + return objectMapper.writeValueAsBytes(object); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public enum MetricType { + SYSTEM_METRIC((byte) 1), + TRANSACTION_METRIC((byte) 2), + ; + + private final byte value; + + MetricType(final byte value) { + this.value = value; + } + } +} diff --git a/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java b/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java new file mode 100644 index 00000000..78c25eed --- /dev/null +++ b/java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java @@ -0,0 +1,53 @@ +package org.traffichunter.javaagent.websocket.metadata; + +import java.time.Instant; +import org.traffichunter.javaagent.commons.status.AgentStatus; + +public record Metadata( + String agentId, + String agentVersion, + String agentName, + Instant startTime, + AgentStatus status) { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String agentId; + private String agentVersion; + private String agentName; + private Instant startTime; + private AgentStatus status; + + public Builder agentId(String agentId) { + this.agentId = agentId; + return this; + } + + public Builder agentVersion(String agentVersion) { + this.agentVersion = agentVersion; + return this; + } + + public Builder agentName(String agentName) { + this.agentName = agentName; + return this; + } + + public Builder startTime(Instant startTime) { + this.startTime = startTime; + return this; + } + + public Builder status(AgentStatus status) { + this.status = status; + return this; + } + + public Metadata build() { + return new Metadata(agentId, agentVersion, agentName, startTime, status); + } + } +} diff --git a/java-apm-agent/plugin/build.gradle b/java-apm-agent/plugin/build.gradle new file mode 100644 index 00000000..418f1ce6 --- /dev/null +++ b/java-apm-agent/plugin/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +subprojects { + apply plugin: 'java' + + dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation 'net.bytebuddy:byte-buddy:1.15.5' + + implementation 'io.opentelemetry:opentelemetry-api:1.45.0' + implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.properties b/java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1af9e093 --- /dev/null +++ b/java-apm-agent/plugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/java-apm-agent/plugin/plugin-sdk/build.gradle b/java-apm-agent/plugin/plugin-sdk/build.gradle new file mode 100644 index 00000000..a121ab7a --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin.plugin-sdk' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java new file mode 100644 index 00000000..e88902ff --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java @@ -0,0 +1,10 @@ +package org.traffichunter.javaagent.plugin.sdk.constant; + +public enum PluginConstant { + + SPRING_BOOT, + MYSQL, + KAFKA, + ELASTICSEARCH, + POSTGRES +} diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java new file mode 100644 index 00000000..e9ef6630 --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java @@ -0,0 +1,9 @@ +package org.traffichunter.javaagent.plugin.sdk.instrumentation; + +import net.bytebuddy.agent.builder.AgentBuilder; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.type.TypeInstrumentation; + +public interface PluginInstrumentation extends TypeInstrumentation { + + void transform(AgentBuilder.Transformer transformer, ClassLoader classLoader); +} diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java new file mode 100644 index 00000000..730431dc --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java @@ -0,0 +1,16 @@ +package org.traffichunter.javaagent.plugin.sdk.instrumentation.type; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; + +public interface TypeInstrumentation { + + default ElementMatcher.Junction classLoaderMatcher() { return any(); } + + ElementMatcher typeMatcher(); + + Junction ignorePackage(); +} diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java new file mode 100644 index 00000000..1ff60c68 --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java @@ -0,0 +1,8 @@ +package org.traffichunter.javaagent.plugin.sdk.loader; + +import java.util.List; + +public interface PluginLoader

{ + + List

loadModules(ClassLoader classLoader); +} diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java new file mode 100644 index 00000000..fa750825 --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java @@ -0,0 +1,23 @@ +package org.traffichunter.javaagent.plugin.sdk.loader; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; + +public class TrafficHunterPluginLoader implements PluginLoader { + + @Override + public List loadModules(final ClassLoader classLoader) { + + List loadPlugIn = new ArrayList<>(); + + ServiceLoader loader = ServiceLoader.load(PluginInstrumentation.class, classLoader); + + for(PluginInstrumentation plugIn : loader) { + loadPlugIn.add(plugIn); + } + + return loadPlugIn; + } +} diff --git a/java-apm-agent/plugin/spring-webmvc/build.gradle b/java-apm-agent/plugin/spring-webmvc/build.gradle new file mode 100644 index 00000000..807f1925 --- /dev/null +++ b/java-apm-agent/plugin/spring-webmvc/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin.spring-webmvc' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':java-apm-agent:plugin:plugin-sdk') +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java new file mode 100644 index 00000000..37c0c7bd --- /dev/null +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java @@ -0,0 +1,36 @@ +package org.traffichunter.javaagent.plugin.spring.webmvc; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; + +public class SpringWebMvcPluginInstrumentation implements PluginInstrumentation { + + @Override + public void transform(final Transformer transformer, final ClassLoader classLoader) { + + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.springframework.web.servlet.DispatcherServlet"); + } + + @Override + public Junction ignorePackage() { + return null; + } + + public static class SpringWebMvcDispatcherServletAdvice { + + @OnMethodEnter + public static void enter() { + + } + } +} diff --git a/java-apm-agent/versions.gradle b/java-apm-agent/versions.gradle new file mode 100644 index 00000000..2a9e7fec --- /dev/null +++ b/java-apm-agent/versions.gradle @@ -0,0 +1,3 @@ +ext { + AgentReleaseVersion = 'v1.1.0' +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e5de471d..14c924a0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,18 @@ rootProject.name = 'traffic_hunter' -include(':java-agent') + +// agent +include(':java-apm-agent') +include(':java-apm-agent:plugin') +include(':java-apm-agent:plugin:plugin-sdk') +include(':java-apm-agent:plugin:spring-webmvc') +include(':java-apm-agent:java-agent-trace') +include(':java-apm-agent:java-agent-retry') +include(':java-apm-agent:java-agent-websocket') +include(':java-apm-agent:java-agent-commons') +include(':java-apm-agent:java-agent-bootstrap') +include(':java-apm-agent:java-agent-event') +include(':java-apm-agent:java-agent-jmx') + +// server include(':server') -include(':java-agent:plugin') -include(':java-agent:plugin:plugin-sdk') -include(':java-agent:plugin:spring-webmvc') -include(':java-agent:java-agent-trace') -include(':java-agent:java-agent-retry') -include(':java-agent:java-agent-websocket') -include(':java-agent:java-agent-commons') -include(':java-agent:java-agent-bootstrap') -include(':java-agent:java-agent-event') \ No newline at end of file + From 8afcdde50358132ef84532b18c31fc126911e832 Mon Sep 17 00:00:00 2001 From: swager253 Date: Sun, 5 Jan 2025 16:21:29 +0900 Subject: [PATCH 05/25] =?UTF-8?q?(#22)=20java-agent=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java-agent/build.gradle | 60 ----- java-agent/gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - java-agent/gradlew | 249 ------------------ java-agent/gradlew.bat | 92 ------- java-agent/java-agent-bootstrap/build.gradle | 54 ---- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-bootstrap/gradlew | 234 ---------------- java-agent/java-agent-bootstrap/gradlew.bat | 89 ------- .../javaagent/bootstrap/BootstrapMain.java | 58 ---- .../bootstrap/banner/AsciiBanner.java | 65 ----- .../engine/AgentExecutionEngine.java | 232 ---------------- .../TrafficHunterAgentShutdownHook.java | 79 ------ .../collect/AbstractMBeanMetricCollector.java | 94 ------- .../engine/collect/MetricCollectSupport.java | 132 ---------- .../engine/collect/MetricCollector.java | 54 ---- .../dbcp/hikari/HikariCPMetricCollector.java | 58 ---- .../systeminfo/cpu/CpuMetricCollector.java | 72 ----- .../gc/GarbageCollectionMetricCollector.java | 74 ------ .../memory/MemoryMetricCollector.java | 97 ------- .../runtime/RuntimeMetricCollector.java | 74 ------ .../thread/ThreadMetricCollector.java | 72 ----- .../web/tomcat/TomcatMetricCollector.java | 86 ------ .../context/AgentExecutableContext.java | 68 ----- .../ConfigurableContextInitializer.java | 189 ------------- .../TrafficHunterAgentExecutableContext.java | 191 -------------- .../engine/env/ConfigurableEnvironment.java | 52 ---- .../bootstrap/engine/env/Environment.java | 54 ---- .../env/yaml/YamlConfigurableEnvironment.java | 120 --------- .../env/yaml/bind/RelaxedBindingUtils.java | 59 ----- .../env/yaml/root/RootYamlProperty.java | 50 ---- .../env/yaml/root/agent/AgentSubProperty.java | 102 ------- .../root/agent/retry/RetrySubProperty.java | 68 ----- .../retry/backoff/BackOffSubProperty.java | 58 ---- .../instrument/annotation/AnnotationPath.java | 57 ---- .../instrument/bootstrap/BootState.java | 46 ---- .../classloading/THAgentClassLoader.java | 59 ----- .../instrument/locator/AgentLocator.java | 59 ----- .../bootstrap/engine/jvm/JVMSelector.java | 86 ------ .../bootstrap/engine/lifecycle/LifeCycle.java | 88 ------- .../engine/property/TrafficHunterAgent.java | 122 --------- .../property/TrafficHunterAgentProperty.java | 86 ------ .../FaultTolerantTrafficHunterAgent.java | 89 ------- .../bootstrap/engine/sender/MetricSender.java | 41 --- .../manager/MetricSendSessionManager.java | 198 -------------- .../websocket/AgentSystemMetricSender.java | 77 ------ .../AgentTransactionMetricSender.java | 87 ------ .../bootstrap/metadata/AgentMetadata.java | 86 ------ .../bootstrap/metadata/MetadataWrapper.java | 35 --- .../src/main/resources/agent-banner.txt | 11 - java-agent/java-agent-commons/build.gradle | 19 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-commons/gradlew | 234 ---------------- java-agent/java-agent-commons/gradlew.bat | 89 ------- .../commons/dto/dbcp/HikariDbcpInfo.java | 37 --- .../commons/dto/systeminfo/SystemInfo.java | 49 ---- .../dto/systeminfo/cpu/CpuStatusInfo.java | 31 --- .../gc/GarbageCollectionStatusInfo.java | 34 --- .../gc/collections/GarbageCollectionTime.java | 40 --- .../systeminfo/memory/MemoryStatusInfo.java | 33 --- .../systeminfo/runtime/RuntimeStatusInfo.java | 36 --- .../systeminfo/thread/ThreadStatusInfo.java | 35 --- .../dto/web/tomcat/TomcatWebServerInfo.java | 37 --- .../web/tomcat/request/TomcatRequestInfo.java | 37 --- .../tomcat/thread/TomcatThreadPoolInfo.java | 35 --- .../javaagent/commons/status/AgentStatus.java | 34 --- .../javaagent/commons/util/AgentUtil.java | 60 ----- .../javaagent/commons/util/FileUtils.java | 48 ---- .../javaagent/commons/util/UUIDGenerator.java | 82 ------ java-agent/java-agent-event/build.gradle | 21 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-event/gradlew | 234 ---------------- java-agent/java-agent-event/gradlew.bat | 89 ------- .../javaagent/event/AgentEvent.java | 54 ---- .../AgentContextStateEventHandler.java | 43 --- .../listener/AgentStateEventListener.java | 36 --- .../event/object/AgentStateEvent.java | 66 ----- .../event/store/AgentStateEventStore.java | 74 ------ java-agent/java-agent-retry/build.gradle | 21 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-retry/gradlew | 234 ---------------- java-agent/java-agent-retry/gradlew.bat | 89 ------- .../javaagent/retry/RetryHelper.java | 169 ------------ .../retry/backoff/BackOffPolicy.java | 65 ----- .../policy/ExponentialBackOffPolicy.java | 39 --- .../backoff/policy/FixedBackOffPolicy.java | 39 --- java-agent/java-agent-trace/build.gradle | 24 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-trace/gradlew | 234 ---------------- java-agent/java-agent-trace/gradlew.bat | 89 ------- .../javaagent/trace/dto/TraceInfo.java | 85 ------ .../trace/exporter/TraceExporter.java | 78 ------ .../javaagent/trace/manager/TraceManager.java | 72 ----- .../javaagent/trace/queue/TraceQueue.java | 63 ----- java-agent/java-agent-websocket/build.gradle | 26 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - java-agent/java-agent-websocket/gradlew | 234 ---------------- java-agent/java-agent-websocket/gradlew.bat | 89 ------- .../websocket/MetricWebSocketClient.java | 163 ------------ .../SerializationByteArrayConverter.java | 105 -------- .../websocket/metadata/Metadata.java | 53 ---- java-agent/plugin/build.gradle | 28 -- .../plugin/gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - java-agent/plugin/gradlew | 249 ------------------ java-agent/plugin/gradlew.bat | 92 ------- java-agent/plugin/plugin-sdk/build.gradle | 18 -- .../plugin/sdk/constant/PluginConstant.java | 10 - .../PluginInstrumentation.java | 9 - .../type/TypeInstrumentation.java | 16 -- .../plugin/sdk/loader/PluginLoader.java | 8 - .../sdk/loader/TrafficHunterPluginLoader.java | 23 -- java-agent/plugin/spring-webmvc/build.gradle | 18 -- .../SpringWebMvcPluginInstrumentation.java | 36 --- .../src/jmh/java/ygo/benchmark/Benchmark.java | 37 --- .../ygo/traffichunter/agent/AgentStatus.java | 34 --- .../agent/TrafficHunterAgent.java | 123 --------- .../agent/banner/AsciiBanner.java | 65 ----- .../FaultTolerantTrafficHunterAgent.java | 89 ------- .../agent/engine/AgentExecutionEngine.java | 232 ---------------- .../TrafficHunterAgentShutdownHook.java | 79 ------ .../collect/AbstractMBeanMetricCollector.java | 94 ------- .../engine/collect/MetricCollectSupport.java | 132 ---------- .../agent/engine/collect/MetricCollector.java | 54 ---- .../dbcp/hikari/HikariCPMetricCollector.java | 58 ---- .../systeminfo/cpu/CpuMetricCollector.java | 72 ----- .../gc/GarbageCollectionMetricCollector.java | 74 ------ .../memory/MemoryMetricCollector.java | 97 ------- .../runtime/RuntimeMetricCollector.java | 74 ------ .../thread/ThreadMetricCollector.java | 72 ----- .../web/tomcat/TomcatMetricCollector.java | 86 ------ .../context/AgentExecutableContext.java | 68 ----- .../ConfigurableContextInitializer.java | 189 ------------- .../TrafficHunterAgentExecutableContext.java | 191 -------------- .../engine/env/ConfigurableEnvironment.java | 52 ---- .../agent/engine/env/Environment.java | 54 ---- .../env/yaml/YamlConfigurableEnvironment.java | 120 --------- .../env/yaml/bind/RelaxedBindingUtils.java | 59 ----- .../env/yaml/root/RootYamlProperty.java | 50 ---- .../env/yaml/root/agent/AgentSubProperty.java | 102 ------- .../root/agent/retry/RetrySubProperty.java | 68 ----- .../retry/backoff/BackOffSubProperty.java | 58 ---- .../instrument/JavaInstrumentAgentMain.java | 58 ---- .../instrument/annotation/AnnotationPath.java | 57 ---- .../instrument/bootstrap/BootState.java | 46 ---- .../classloading/THAgentClassLoader.java | 59 ----- .../instrument/locator/AgentLocator.java | 59 ----- .../agent/engine/jvm/JVMSelector.java | 86 ------ .../agent/engine/lifecycle/LifeCycle.java | 88 ------- .../engine/metric/dbcp/HikariDbcpInfo.java | 37 --- .../engine/metric/metadata/AgentMetadata.java | 86 ------ .../metric/metadata/MetadataWrapper.java | 35 --- .../engine/metric/systeminfo/SystemInfo.java | 49 ---- .../metric/systeminfo/cpu/CpuStatusInfo.java | 31 --- .../gc/GarbageCollectionStatusInfo.java | 34 --- .../gc/collections/GarbageCollectionTime.java | 40 --- .../systeminfo/memory/MemoryStatusInfo.java | 33 --- .../systeminfo/runtime/RuntimeStatusInfo.java | 36 --- .../systeminfo/thread/ThreadStatusInfo.java | 35 --- .../engine/metric/transaction/TraceInfo.java | 85 ------ .../metric/transaction/TransactionInfo.java | 51 ---- .../web/tomcat/TomcatWebServerInfo.java | 37 --- .../web/tomcat/request/TomcatRequestInfo.java | 37 --- .../tomcat/thread/TomcatThreadPoolInfo.java | 35 --- .../agent/engine/queue/SyncQueue.java | 63 ----- .../agent/engine/sender/MetricSender.java | 41 --- .../manager/MetricSendSessionManager.java | 183 ------------- .../websocket/AgentSystemMetricSender.java | 77 ------ .../AgentTransactionMetricSender.java | 87 ------ .../traffichunter/agent/event/AgentEvent.java | 54 ---- .../AgentContextStateEventHandler.java | 43 --- .../listener/AgentStateEventListener.java | 36 --- .../agent/event/object/AgentStateEvent.java | 66 ----- .../event/store/AgentStateEventStore.java | 74 ------ .../property/TrafficHunterAgentProperty.java | 86 ------ .../ygo/traffichunter/http/HttpBuilder.java | 101 ------- .../traffichunter/http/status/HttpStatus.java | 54 ---- .../ygo/traffichunter/retry/RetryHelper.java | 169 ------------ .../retry/backoff/BackOffPolicy.java | 68 ----- .../policy/ExponentialBackOffPolicy.java | 39 --- .../backoff/policy/FixedBackOffPolicy.java | 39 --- .../trace/opentelemetry/TraceExporter.java | 79 ------ .../trace/opentelemetry/TraceManager.java | 72 ----- .../ygo/traffichunter/util/AgentUtil.java | 60 ----- .../ygo/traffichunter/util/FileUtils.java | 49 ---- .../ygo/traffichunter/util/UUIDGenerator.java | 83 ------ .../websocket/MetricWebSocketClient.java | 163 ------------ .../SerializationByteArrayConverter.java | 133 ---------- .../src/main/resources/agent-banner.txt | 11 - .../src/test/java/ygo/AbstractTest.java | 8 - .../agent/TrafficHunterAgentTest.java | 33 --- .../FaultTolerantTrafficHunterAgentTest.java | 15 -- .../engine/classloader/ClassLoaderTest.java | 15 -- .../ConfigurableContextInitializerTest.java | 36 --- .../yaml/YamlConfigurableEnvironmentTest.java | 38 --- .../AgentTransactionMetricSenderTest.java | 54 ---- .../tracking/TransactionTrackingTest.java | 27 -- .../traffichunter/banner/AsciiBannerTest.java | 18 -- .../engine/AgentExecutionEngineTest.java | 16 -- .../traffichunter/http/HttpBuilderTest.java | 55 ---- .../ygo/traffichunter/util/AgentUtilTest.java | 61 ----- .../ygo/traffichunter/util/FileUtilsTest.java | 17 -- .../traffichunter/util/UUIDGeneratorTest.java | 16 -- .../websocket/MetricWebSocketClientTest.java | 33 --- .../SerializationByteArrayConverterTest.java | 91 ------- 211 files changed, 14438 deletions(-) delete mode 100644 java-agent/build.gradle delete mode 100644 java-agent/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/gradlew delete mode 100644 java-agent/gradlew.bat delete mode 100644 java-agent/java-agent-bootstrap/build.gradle delete mode 100644 java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-bootstrap/gradlew delete mode 100644 java-agent/java-agent-bootstrap/gradlew.bat delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java delete mode 100644 java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt delete mode 100644 java-agent/java-agent-commons/build.gradle delete mode 100644 java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-commons/gradlew delete mode 100644 java-agent/java-agent-commons/gradlew.bat delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java delete mode 100644 java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java delete mode 100644 java-agent/java-agent-event/build.gradle delete mode 100644 java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-event/gradlew delete mode 100644 java-agent/java-agent-event/gradlew.bat delete mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java delete mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java delete mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java delete mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java delete mode 100644 java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java delete mode 100644 java-agent/java-agent-retry/build.gradle delete mode 100644 java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-retry/gradlew delete mode 100644 java-agent/java-agent-retry/gradlew.bat delete mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java delete mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java delete mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java delete mode 100644 java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java delete mode 100644 java-agent/java-agent-trace/build.gradle delete mode 100644 java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-trace/gradlew delete mode 100644 java-agent/java-agent-trace/gradlew.bat delete mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java delete mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java delete mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java delete mode 100644 java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java delete mode 100644 java-agent/java-agent-websocket/build.gradle delete mode 100644 java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/java-agent-websocket/gradlew delete mode 100644 java-agent/java-agent-websocket/gradlew.bat delete mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java delete mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java delete mode 100644 java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java delete mode 100644 java-agent/plugin/build.gradle delete mode 100644 java-agent/plugin/gradle/wrapper/gradle-wrapper.jar delete mode 100644 java-agent/plugin/gradle/wrapper/gradle-wrapper.properties delete mode 100755 java-agent/plugin/gradlew delete mode 100644 java-agent/plugin/gradlew.bat delete mode 100644 java-agent/plugin/plugin-sdk/build.gradle delete mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java delete mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java delete mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java delete mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java delete mode 100644 java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java delete mode 100644 java-agent/plugin/spring-webmvc/build.gradle delete mode 100644 java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java delete mode 100644 java-agent/src/jmh/java/ygo/benchmark/Benchmark.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/AgentStatus.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/TrafficHunterAgent.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/banner/AsciiBanner.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgent.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/TrafficHunterAgentShutdownHook.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/AbstractMBeanMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollectSupport.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/dbcp/hikari/HikariCPMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/cpu/CpuMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/memory/MemoryMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/thread/ThreadMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/web/tomcat/TomcatMetricCollector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/context/AgentExecutableContext.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/context/execute/TrafficHunterAgentExecutableContext.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/ConfigurableEnvironment.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/Environment.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironment.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/bind/RelaxedBindingUtils.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/RootYamlProperty.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/AgentSubProperty.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/RetrySubProperty.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/JavaInstrumentAgentMain.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/annotation/AnnotationPath.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/bootstrap/BootState.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/classloading/THAgentClassLoader.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/locator/AgentLocator.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/jvm/JVMSelector.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/lifecycle/LifeCycle.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/dbcp/HikariDbcpInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/AgentMetadata.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/MetadataWrapper.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/SystemInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/cpu/CpuStatusInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/GarbageCollectionStatusInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/collections/GarbageCollectionTime.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/memory/MemoryStatusInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/runtime/RuntimeStatusInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/thread/ThreadStatusInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TraceInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TransactionInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/TomcatWebServerInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/request/TomcatRequestInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/thread/TomcatThreadPoolInfo.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/queue/SyncQueue.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/MetricSender.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/manager/MetricSendSessionManager.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentSystemMetricSender.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSender.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/event/AgentEvent.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/event/context/AgentContextStateEventHandler.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/event/listener/AgentStateEventListener.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/event/object/AgentStateEvent.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/event/store/AgentStateEventStore.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/agent/property/TrafficHunterAgentProperty.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/http/HttpBuilder.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/http/status/HttpStatus.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/retry/RetryHelper.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/retry/backoff/BackOffPolicy.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/ExponentialBackOffPolicy.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/FixedBackOffPolicy.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/util/AgentUtil.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/util/FileUtils.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/util/UUIDGenerator.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/websocket/MetricWebSocketClient.java delete mode 100644 java-agent/src/main/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverter.java delete mode 100644 java-agent/src/main/resources/agent-banner.txt delete mode 100644 java-agent/src/test/java/ygo/AbstractTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/TrafficHunterAgentTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgentTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/engine/classloader/ClassLoaderTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializerTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironmentTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSenderTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/agent/tracking/TransactionTrackingTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/banner/AsciiBannerTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/engine/AgentExecutionEngineTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/http/HttpBuilderTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/util/AgentUtilTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/util/FileUtilsTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/util/UUIDGeneratorTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/websocket/MetricWebSocketClientTest.java delete mode 100644 java-agent/src/test/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverterTest.java diff --git a/java-agent/build.gradle b/java-agent/build.gradle deleted file mode 100644 index 8275601d..00000000 --- a/java-agent/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -plugins { - id 'java' - id 'com.github.johnrengelman.shadow' version '8.1.1' - id "me.champeau.jmh" version "0.7.2" -} - -group = 'ygo' -version = '0.0.1-SNAPSHOT' - -repositories { - mavenCentral() -} - -shadowJar { - - manifest { - attributes( - 'Premain-Class': 'ygo.traffichunter.agent.engine.instrument.JavaInstrumentAgentMain', - 'Can-Redefine-Classes': 'true', - 'Can-Retransform-Classes': 'true', - 'Permissions': 'all-permissions' - ) - } - - archiveFileName.set("traffic-hunter-agent-v1.0.0.jar") -} - -jmh { - fork = 1 - warmupIterations = 1 - iterations = 1 -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - testImplementation 'org.openjdk.jmh:jmh-core:1.37' - testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37' - - jmh 'org.openjdk.jmh:jmh-core:0.9' - jmh 'org.openjdk.jmh:jmh-generator-annprocess:0.9' - jmh 'org.openjdk.jmh:jmh-generator-bytecode:0.9' - - implementation 'org.slf4j:slf4j-api:2.0.7' - implementation 'org.slf4j:slf4j-simple:2.0.7' - implementation 'org.java-websocket:Java-WebSocket:1.5.7' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' - implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' - implementation 'org.yaml:snakeyaml:2.3' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' - implementation 'net.bytebuddy:byte-buddy:1.15.5' - implementation 'io.opentelemetry:opentelemetry-api:1.45.0' - implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/gradle/wrapper/gradle-wrapper.jar b/java-agent/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/java-agent/gradle/wrapper/gradle-wrapper.properties b/java-agent/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 1af9e093..00000000 --- a/java-agent/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/java-agent/gradlew b/java-agent/gradlew deleted file mode 100755 index 1aa94a42..00000000 --- a/java-agent/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/gradlew.bat b/java-agent/gradlew.bat deleted file mode 100644 index 93e3f59f..00000000 --- a/java-agent/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-bootstrap/build.gradle b/java-agent/java-agent-bootstrap/build.gradle deleted file mode 100644 index a6d2c63c..00000000 --- a/java-agent/java-agent-bootstrap/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -plugins { - id 'java' - id 'com.github.johnrengelman.shadow' version '8.1.1' -} - -group = 'org.traffichunter.javaagent.bootstrap' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -shadowJar { - - manifest { - attributes( - 'Premain-Class': 'org.traffichunter.javaagent.bootstrap.BootstrapMain', - 'Can-Redefine-Classes': 'true', - 'Can-Retransform-Classes': 'true', - 'Permissions': 'all-permissions' - ) - } - - archiveFileName.set("traffic-hunter-agent-v1.0.0.jar") -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-agent:java-agent-event') - implementation project(':java-agent:java-agent-commons') - implementation project(':java-agent:java-agent-websocket') - implementation project(':java-agent:java-agent-trace') - implementation project(':java-agent:java-agent-retry') - implementation project(':java-agent:plugin') - - implementation 'io.opentelemetry:opentelemetry-api:1.45.0' - implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' - - implementation 'org.slf4j:slf4j-api:2.0.7' - implementation 'org.slf4j:slf4j-simple:2.0.7' - implementation 'org.yaml:snakeyaml:2.3' - implementation 'org.java-websocket:Java-WebSocket:1.5.7' - implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' - implementation 'net.bytebuddy:byte-buddy:1.15.5' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-bootstrap/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-bootstrap/gradlew.bat b/java-agent/java-agent-bootstrap/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-bootstrap/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java deleted file mode 100644 index cd761694..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap; - -import java.lang.instrument.Instrumentation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.AgentExecutionEngine; -import org.traffichunter.javaagent.bootstrap.engine.env.Environment; -import org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap.BootState; - -/** - * main. - * @author yungwang-o - * @version 1.0.0 - */ -public class BootstrapMain { - - private static final Logger log = LoggerFactory.getLogger(BootstrapMain.class); - - private static final BootState STATE = new BootState(); - - public static void premain(String agentArgs, Instrumentation inst) { - - final boolean success = STATE.start(); - if(!success) { - log.error("traffic-hunter-agent-bootstrap already started. skipping agent loading."); - return; - } - - AgentExecutionEngine.run(Environment.SYSTEM_PROFILE.systemProfile(), inst); - } - - public static void agentmain(String agentArgs, Instrumentation inst) { - premain(agentArgs, inst); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java deleted file mode 100644 index 3d27b91b..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/banner/AsciiBanner.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.banner; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Objects; -import java.util.stream.Collectors; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; - -/** - * banner print. - * @author yungwang-o - * @version 1.0.0 - */ -public class AsciiBanner { - - private static final String BANNER_NAME = "agent-banner.txt"; - - public void print(final AgentMetadata metadata) { - try (final InputStream in = AsciiBanner.class.getClassLoader().getResourceAsStream(BANNER_NAME)) { - - BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(in))); - - String banner = reader.lines() - .map(line -> line - .replace("${version}", metadata.agentVersion()) - .replace("${java.version}", System.getProperty("java.version")) - .replace("${java.specification}", System.getProperty("java.specification.version")) - .replace("${jdk}", System.getProperty("java.vendor")) - .replace("${time}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))) - .collect(Collectors.joining(System.lineSeparator())); - - System.out.println(banner + "\n"); - - } catch (IOException ignored) { - - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java deleted file mode 100644 index 1761c79d..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine; - -import java.lang.instrument.Instrumentation; -import java.time.Duration; -import java.time.Instant; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.banner.AsciiBanner; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.context.execute.TrafficHunterAgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.lifecycle.LifeCycle; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.trace.manager.TraceManager; -import org.traffichunter.javaagent.trace.queue.TraceQueue; - -/** - *

- * The {@code AgentExecutionEngine} class is the core execution engine responsible for - * initializing, configuring, running, and shutting down the TrafficHunter Agent. - * This class encapsulates all the necessary steps to bootstrap the agent, manage its - * lifecycle, and ensure proper cleanup upon application termination. - *

- *

Purpose:

- *
    - *
  • Initialize and configure the TrafficHunter Agent with environment-specific settings.
  • - *
  • Run the agent in a dedicated thread to monitor and manage traffic.
  • - *
  • Register and manage shutdown hooks to ensure proper cleanup during termination.
  • - *
- * - *

Key Components:

- *
    - *
  • {@link TrafficHunterAgentShutdownHook} - Manages graceful shutdown of resources.
  • - *
  • {@link ConfigurableEnvironment} - Loads environment-specific configurations.
  • - *
  • {@link AgentRunner} - Executes the agent's core logic in a separate thread.
  • - *
- * - *

Limitations:

- *
    - *
  • This class assumes that the {@code Instrumentation} object is correctly provided at runtime.
  • - *
  • Agent initialization failures may result in partial cleanup if not handled carefully.
  • - *
- * - * @see TrafficHunterAgentShutdownHook - * @see AgentRunner - * @see ConfigurableEnvironment - * @see Instrumentation - * @author yungwang-o - * @version 1.0.0 - */ -public final class AgentExecutionEngine { - - private static final Logger log = LoggerFactory.getLogger(AgentExecutionEngine.class); - - private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); - - private final AsciiBanner asciiBanner = new AsciiBanner(); - - private final ConfigurableEnvironment environment; - - private final Instrumentation inst; - - private AgentExecutionEngine(final String args, final Instrumentation inst) { - this.inst = inst; - this.environment = new YamlConfigurableEnvironment(args); - } - - /** - * Initializes and executes the TrafficHunter Agent. - *

This method performs the following tasks:

- *
    - *
  • Loads environment-specific configurations using {@link YamlConfigurableEnvironment}.
  • - *
  • Initializes the agent's execution context and metadata.
  • - *
  • Registers shutdown hooks for cleanup during termination.
  • - *
  • Starts the agent's core logic in a dedicated thread.
  • - *
- */ - private void run() { - StartUp startUp = new StartUp(); - Instant startTime = startUp.getStartTime(); - final AgentExecutableContext context = new TrafficHunterAgentExecutableContext(environment, shutdownHook); - if(!shutdownHook.isEnabledShutdownHook()) { - shutdownHook.enableShutdownHook(); - } - ConfigurableContextInitializer configurableContextInitializer = context.init(); - configurableContextInitializer.retransform(inst); - TrafficHunterAgentProperty property = configurableContextInitializer.property(); - AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( - startTime, - AgentStatus.INITIALIZED - ); - context.addAgentStateEventListener(metadata); - asciiBanner.print(metadata); - AgentRunner runner = new AgentRunner(property, context, metadata); - Thread runnerThread = new Thread(runner); - runnerThread.setName("TrafficHunterAgentRunnerThread"); - if(context.isInit()) { - log.info("Agent initialization completed."); - runnerThread.start(); - registryShutdownHook(context, runner); - context.close(); - } - - log.info("Started TrafficHunter Agent in {} second", String.format("%.3f", startUp.getUpTime())); - } - - /** - * Registers shutdown hooks for the agent's cleanup operations. - * - * @param context The execution context of the agent. - * @param runner The agent runner instance responsible for managing execution. - */ - private void registryShutdownHook(final AgentExecutableContext context, final AgentRunner runner) { - shutdownHook.addRuntimeShutdownHook(TraceManager::close); - shutdownHook.addRuntimeShutdownHook(TraceQueue.INSTANCE::removeAll); - shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); - shutdownHook.addRuntimeShutdownHook(runner::close); - } - - public static void run(final String args, final Instrumentation inst) { - new AgentExecutionEngine(System.getProperty(args), inst).run(); - } - - /** - * The {@code StartUp} class extends {@link LifeCycle} to measure the agent's - * startup durations. - * - *

Features:

- *
    - *
  • Tracks the agent's start time and end time.
  • - *
  • Calculates the total uptime.
  • - *
- */ - private static class StartUp extends LifeCycle { - - public StartUp() { - super(); - } - - @Override - public Instant getStartTime() { - return this.startTime; - } - - @Override - public Instant getEndTime() { - if(endTime == null) { - this.endTime = Instant.now(); - } - return endTime; - } - - @Override - public Double getUpTime() { - if(getStartTime() == null && getEndTime() == null) { - throw new IllegalStateException("No start time or end time specified"); - } - - return Duration.between(getStartTime(), getEndTime()).toMillis() / 1_000.0; - } - } - - /** - * The {@code AgentRunner} class implements {@link Runnable} to execute the core logic - * of the TrafficHunter Agent in a separate thread. It initializes the session manager - * and orchestrates agent operations after the target application is loaded. - * - *

Features:

- *
    - *
  • Introduces a delay to ensure the target application is fully loaded before execution.
  • - *
  • Manages the lifecycle of the {@link MetricSendSessionManager}.
  • - *
- */ - private static final class AgentRunner implements Runnable { - - private final MetricSendSessionManager sessionManager; - - public AgentRunner(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.sessionManager = new MetricSendSessionManager(property, context, metadata); - } - - /** - * Executes the agent's core logic. Introduces a delay to ensure that - * the target application is fully loaded before starting. - */ - @Override - public void run() { - try { - log.info("Waiting for Agent Runner..."); - Thread.sleep(8000); - sessionManager.run(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - public void close() { - sessionManager.close(); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java deleted file mode 100644 index 5bec461a..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine; - -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@code TrafficHunterAgentShutdownHook} class is responsible for managing and - * registering shutdown hooks to execute specific actions when the Java application - * terminates. This class allows users to add custom {@link Runnable} actions that - * will be executed gracefully during the shutdown process. - * - * @see Runtime#addShutdownHook(Thread) - * @see Runnable - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgentShutdownHook implements Runnable { - - private static final Logger log = LoggerFactory.getLogger(TrafficHunterAgentShutdownHook.class); - - private volatile boolean enabledShutdownHook = false; - - private final Set actions = ConcurrentHashMap.newKeySet(); - - public void enableShutdownHook() { - enabledShutdownHook = true; - } - - public void addRuntimeShutdownHook(final Runnable action) { - actions.add(action); - } - - @Override - public void run() { - log.info("register shutdown hook"); - - if(!enabledShutdownHook) { - log.info("shutdown hook not enabled"); - return; - } - - for(Runnable action : actions) { - Runtime.getRuntime().addShutdownHook(new Thread(action, nameShutdownThread(action.getClass().getName()))); - } - } - - public boolean isEnabledShutdownHook() { - return enabledShutdownHook; - } - - private static String nameShutdownThread(final String threadName) { - return threadName + "-ShutdownHook"; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java deleted file mode 100644 index 41c9483f..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/AbstractMBeanMetricCollector.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect; - -import java.lang.management.ManagementFactory; -import java.util.Set; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -/** - * The {@code AbstractMBeanMetricCollector} abstract class provides a base implementation - * for collecting metrics using the JMX {@link MBeanServer}. - * - *

Features:

- *
    - *
  • Extends {@link MetricCollector} for metric collection.
  • - *
  • Provides helper methods to query MBeans and retrieve attributes.
  • - *
  • Handles exceptions gracefully through the nested {@link MetricCollectionException} class.
  • - *
- * - * @see MetricCollector - * @see MBeanServer - * - * @author yungwang-o - * @version 1.0.0 - */ -public abstract class AbstractMBeanMetricCollector implements MetricCollector { - - protected final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); - - protected Set findObjectName(final String objectName) { - try { - return mBeanServer.queryNames(new ObjectName(objectName), null); - } catch (MalformedObjectNameException e) { - throw new RuntimeException(e); - } - } - - protected
A getAttribute(final ObjectName name, final String attribute, final Class type) { - try { - - return type.cast(mBeanServer.getAttribute(name, attribute)); - } catch (Exception e) { - throw new MetricCollectionException("Failed to get attribute: " + attribute, e); - } - } - - public static class MetricCollectionException extends RuntimeException { - - public MetricCollectionException() { - super(); - } - - public MetricCollectionException(final String message) { - super(message); - } - - public MetricCollectionException(final String message, final Throwable cause) { - super(message, cause); - } - - public MetricCollectionException(final Throwable cause) { - super(cause); - } - - protected MetricCollectionException(final String message, final Throwable cause, - final boolean enableSuppression, - final boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java deleted file mode 100644 index aa9b3198..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollectSupport.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect; - -import java.io.IOException; -import java.time.Instant; -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.collect.dbcp.hikari.HikariCPMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.cpu.CpuMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.gc.GarbageCollectionMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory.MemoryMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.runtime.RuntimeMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.thread.ThreadMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.web.tomcat.TomcatMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.jvm.JVMSelector; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.commons.dto.systeminfo.SystemInfo; - -/** - * The {@code MetricCollectSupport} class provides functionality for collecting system metrics - * from the local JVM or a target JVM via JMX. - * - *

Features:

- *
    - *
  • Integrates multiple metric collectors for memory, CPU, threads, garbage collection, - * runtime, Tomcat, and HikariCP metrics.
  • - *
  • Supports collecting metrics from the local JVM or a remote JVM identified by a JMX path.
  • - *
  • Combines collected metrics into a unified {@link SystemInfo} object.
  • - *
- * - * @see SystemInfo - * @see MemoryMetricCollector - * @see CpuMetricCollector - * @see ThreadMetricCollector - * @see GarbageCollectionMetricCollector - * @see RuntimeMetricCollector - * @see TomcatMetricCollector - * @see HikariCPMetricCollector - * - * @author yungwang-o - * @version 1.0.0 - */ - -public class MetricCollectSupport { - - private static final Logger log = LoggerFactory.getLogger(MetricCollectSupport.class); - - private final MemoryMetricCollector collectorMemory; - - private final CpuMetricCollector collectorCpu; - - private final ThreadMetricCollector collectorThread; - - private final GarbageCollectionMetricCollector collectorGC; - - private final RuntimeMetricCollector collectorRuntime; - - private final TomcatMetricCollector collectorTomcat; - - private final HikariCPMetricCollector collectorHikari; - - public MetricCollectSupport(final TrafficHunterAgentProperty property) { - this.collectorMemory = new MemoryMetricCollector(); - this.collectorCpu = new CpuMetricCollector(); - this.collectorThread = new ThreadMetricCollector(); - this.collectorGC = new GarbageCollectionMetricCollector(); - this.collectorRuntime = new RuntimeMetricCollector(); - this.collectorTomcat = new TomcatMetricCollector(property); - this.collectorHikari = new HikariCPMetricCollector(); - } - - public SystemInfo collect(final String targetJVMPath) { - - try (final JMXConnector jmxConnector = JMXConnectorFactory.connect(JVMSelector.getVMXServiceUrl(targetJVMPath))) { - - final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection(); - - return new SystemInfo( - Instant.now(), - collectorMemory.collect(mbsc), - collectorThread.collect(mbsc), - collectorCpu.collect(mbsc), - collectorGC.collect(mbsc), - collectorRuntime.collect(mbsc), - collectorTomcat.collect(mbsc), - collectorHikari.collect(mbsc) - ); - - } catch (IOException e) { - log.error("Failed to start local management agent = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - public SystemInfo collect() { - return new SystemInfo( - Instant.now(), - collectorMemory.collect(), - collectorThread.collect(), - collectorCpu.collect(), - collectorGC.collect(), - collectorRuntime.collect(), - collectorTomcat.collect(), - collectorHikari.collect() - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java deleted file mode 100644 index deb7bc79..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/MetricCollector.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect; - -import java.util.List; -import javax.management.MBeanServerConnection; - -/** - * The {@code MetricCollector} interface defines a contract for collecting metrics. - * Implementations can collect metrics from various sources, such as the local JVM - * or an MBeanServer. - * - *

Features:

- *
    - *
  • Provides methods for collecting a single metric or all available metrics.
  • - *
  • Includes default implementations for optional operations.
  • - *
- * - * @param The type of metric being collected. - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface MetricCollector { - - default T collect(MBeanServerConnection mbsc) { - return null; - } - - T collect(); - - default List collectAll() {return null;} -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java deleted file mode 100644 index 8b183383..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/dbcp/hikari/HikariCPMetricCollector.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.dbcp.hikari; - -import javax.management.ObjectName; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.commons.dto.dbcp.HikariDbcpInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class HikariCPMetricCollector extends AbstractMBeanMetricCollector { - - @Override - public HikariDbcpInfo collect() { - try { - - //The default name for a Hikari connection pool is "HikariPool-1" - ObjectName hikariInfo = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)"); - - int activeConnections = getAttribute(hikariInfo, "ActiveConnections", Integer.class); - int idleConnections = getAttribute(hikariInfo, "IdleConnections", Integer.class); - int totalConnections = getAttribute(hikariInfo, "TotalConnections", Integer.class); - int threadsAwaitingConnections = getAttribute(hikariInfo, "ThreadsAwaitingConnection", Integer.class); - - return new HikariDbcpInfo( - activeConnections, - idleConnections, - totalConnections, - threadsAwaitingConnections - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java deleted file mode 100644 index 9b1e47a0..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/cpu/CpuMetricCollector.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.cpu; - -import com.sun.management.OperatingSystemMXBean; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.commons.dto.systeminfo.cpu.CpuStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class CpuMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(CpuStatusInfo.class.getName()); - - @Override - public CpuStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final OperatingSystemMXBean osMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, - OperatingSystemMXBean.class - ); - - return new CpuStatusInfo( - osMXBean.getCpuLoad(), - osMXBean.getProcessCpuLoad(), - osMXBean.getAvailableProcessors() - ); - } catch (IOException e) { - log.warning("Failed to get cpu metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public CpuStatusInfo collect() { - final OperatingSystemMXBean osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - - return new CpuStatusInfo( - osMXBean.getCpuLoad(), - osMXBean.getProcessCpuLoad(), - osMXBean.getAvailableProcessors() - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java deleted file mode 100644 index 956fe472..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.gc; - -import java.io.IOException; -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.util.List; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory.MemoryMetricCollector; -import org.traffichunter.javaagent.commons.dto.systeminfo.gc.GarbageCollectionStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections.GarbageCollectionTime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class GarbageCollectionMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); - - @Override - public GarbageCollectionStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final List mxBeans = ManagementFactory.getPlatformMXBeans(mbsc, - GarbageCollectorMXBean.class); - - final List garbageCollectionTimes = mxBeans - .stream() - .map(GarbageCollectionTime::new) - .toList(); - - return new GarbageCollectionStatusInfo(garbageCollectionTimes); - } catch (IOException e) { - log.warning("Failed to get GC metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public GarbageCollectionStatusInfo collect() { - final List mxBeans = ManagementFactory.getGarbageCollectorMXBeans(); - - final List garbageCollectionTimes = mxBeans - .stream() - .map(GarbageCollectionTime::new) - .toList(); - - return new GarbageCollectionStatusInfo(garbageCollectionTimes); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java deleted file mode 100644 index 03f77a37..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/memory/MemoryMetricCollector.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.memory; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo.MemoryUsage; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class MemoryMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); - - @Override - public MemoryStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.MEMORY_MXBEAN_NAME, - MemoryMXBean.class - ); - - final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - - return new MemoryStatusInfo( - new MemoryUsage( - heapMemoryUsage.getInit(), - heapMemoryUsage.getUsed(), - heapMemoryUsage.getCommitted(), - heapMemoryUsage.getMax() - ), - new MemoryUsage( - nonHeapMemoryUsage.getInit(), - nonHeapMemoryUsage.getUsed(), - nonHeapMemoryUsage.getCommitted(), - nonHeapMemoryUsage.getMax() - ) - ); - } catch (IOException e) { - log.warning("Failed to get heap memory metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public MemoryStatusInfo collect() { - final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); - - final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - - return new MemoryStatusInfo( - new MemoryUsage( - heapMemoryUsage.getInit(), - heapMemoryUsage.getUsed(), - heapMemoryUsage.getCommitted(), - heapMemoryUsage.getMax() - ), - new MemoryUsage( - nonHeapMemoryUsage.getInit(), - nonHeapMemoryUsage.getUsed(), - nonHeapMemoryUsage.getCommitted(), - nonHeapMemoryUsage.getMax() - ) - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java deleted file mode 100644 index ef9570d8..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.runtime; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.commons.dto.systeminfo.runtime.RuntimeStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RuntimeMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(RuntimeStatusInfo.class.getName()); - - @Override - public RuntimeStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.RUNTIME_MXBEAN_NAME, - RuntimeMXBean.class - ); - - return new RuntimeStatusInfo( - runtimeMXBean.getStartTime(), - runtimeMXBean.getUptime(), - runtimeMXBean.getVmName(), - runtimeMXBean.getVmVersion() - ); - } catch (IOException e) { - log.warning("Failed to get runtime metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public RuntimeStatusInfo collect() { - final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); - - return new RuntimeStatusInfo( - runtimeMXBean.getStartTime(), - runtimeMXBean.getUptime(), - runtimeMXBean.getVmName(), - runtimeMXBean.getVmVersion() - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java deleted file mode 100644 index 439390ac..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/systeminfo/thread/ThreadMetricCollector.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.systeminfo.thread; - -import com.sun.management.ThreadMXBean; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.commons.dto.systeminfo.thread.ThreadStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class ThreadMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(ThreadMetricCollector.class.getName()); - - @Override - public ThreadStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.THREAD_MXBEAN_NAME, - ThreadMXBean.class - ); - - return new ThreadStatusInfo( - threadMXBean.getThreadCount(), - threadMXBean.getPeakThreadCount(), - threadMXBean.getTotalStartedThreadCount() - ); - } catch (IOException e) { - log.warning("Failed to get thread metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public ThreadStatusInfo collect() { - final ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); - - return new ThreadStatusInfo( - threadMXBean.getThreadCount(), - threadMXBean.getPeakThreadCount(), - threadMXBean.getTotalStartedThreadCount() - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java deleted file mode 100644 index 00b6664c..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/collect/web/tomcat/TomcatMetricCollector.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.collect.web.tomcat; - -import javax.management.ObjectName; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.collect.AbstractMBeanMetricCollector; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.commons.dto.web.tomcat.TomcatWebServerInfo; -import org.traffichunter.javaagent.commons.dto.web.tomcat.request.TomcatRequestInfo; -import org.traffichunter.javaagent.commons.dto.web.tomcat.thread.TomcatThreadPoolInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TomcatMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = LoggerFactory.getLogger(TomcatMetricCollector.class.getName()); - - private final TrafficHunterAgentProperty property; - - public TomcatMetricCollector(final TrafficHunterAgentProperty property) { - this.property = property; - } - - @Override - public TomcatWebServerInfo collect() { - - try { - ObjectName threadPoolMbean = new ObjectName("Tomcat:type=ThreadPool,name=" + getPort()); - ObjectName requestMBean = new ObjectName("Tomcat:type=GlobalRequestProcessor,name=" + getPort()); - - int maxThreads = getAttribute(threadPoolMbean, "maxThreads", Integer.class); - int currentThreads = getAttribute(threadPoolMbean, "currentThreadCount", Integer.class); - int currentThreadsBusy = getAttribute(threadPoolMbean, "currentThreadsBusy", Integer.class); - - int requestCount = getAttribute(requestMBean, "requestCount", Integer.class); - long bytesReceived = getAttribute(requestMBean, "bytesReceived", Long.class); - long bytesSent = getAttribute(requestMBean, "bytesSent", Long.class); - long processingTime = getAttribute(requestMBean, "processingTime", Long.class); - int errorCount = getAttribute(requestMBean, "errorCount", Integer.class); - - return new TomcatWebServerInfo( - new TomcatThreadPoolInfo(maxThreads, currentThreads, currentThreadsBusy), - new TomcatRequestInfo(requestCount, bytesReceived, bytesSent, processingTime, errorCount) - ); - } catch (Exception e) { - log.error("Failed to collect metrics = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - /** - * target uri -> localhost:8080 - *
- * translation -> localhost:8080 -> http-nio-8080 - * @return port - */ - private String getPort() { - final String port = property.targetUri().split(":")[1]; - return String.format("\"http-nio-%s\"", port); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java deleted file mode 100644 index 99d4b0c1..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.context; - -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.event.context.AgentContextStateEventHandler; - -/** - * The {@code AgentExecutableContext} interface defines the contract for managing - * the lifecycle of an agent's execution context. It includes methods for initializing - * the context, managing its state, and handling lifecycle events. - * - *

Features:

- *
    - *
  • Extends {@link AgentContextStateEventHandler} for state event listener management.
  • - *
  • Provides methods to initialize, close, and query the context's status.
  • - *
  • Supports dynamic updates to the agent's execution status.
  • - *
- * - * @see AgentContextStateEventHandler - * @see ConfigurableContextInitializer - * @see ConfigurableEnvironment - * @see AgentStatus - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentExecutableContext extends AgentContextStateEventHandler { - - ConfigurableContextInitializer init(); - - void close(); - - ConfigurableEnvironment getEnvironment(); - - AgentStatus getStatus(); - - boolean isInit(); - - boolean isRunning(); - - boolean isStopped(); - - void setStatus(AgentStatus status); -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java deleted file mode 100644 index 197a32a3..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java +++ /dev/null @@ -1,189 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.context.configuration; - -import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.named; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import java.io.InputStream; -import java.lang.instrument.Instrumentation; -import java.time.Instant; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.builder.AgentBuilder.Default; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.asm.Advice.Enter; -import net.bytebuddy.asm.Advice.OnMethodEnter; -import net.bytebuddy.asm.Advice.OnMethodExit; -import net.bytebuddy.asm.Advice.Origin; -import net.bytebuddy.asm.Advice.Thrown; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; -import net.bytebuddy.matcher.ElementMatchers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.env.Environment; -import org.traffichunter.javaagent.bootstrap.engine.instrument.annotation.AnnotationPath; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.commons.util.UUIDGenerator; -import org.traffichunter.javaagent.trace.exporter.TraceExporter; -import org.traffichunter.javaagent.trace.manager.TraceManager; -import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; - -/** - * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, - * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. - * - *

Features:

- *
    - *
  • Loads properties using a configurable environment.
  • - *
  • Sets up agent metadata based on the current environment and runtime status.
  • - *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • - *
- * - * @see ConfigurableEnvironment - * @see TrafficHunterAgentProperty - * @see ByteBuddy - * @see AgentMetadata - * - * @author yungwang-o - * @version 1.0.0 - */ -public class ConfigurableContextInitializer { - - private static final Logger log = LoggerFactory.getLogger(ConfigurableContextInitializer.class); - - private final ConfigurableEnvironment env; - - public ConfigurableContextInitializer(final ConfigurableEnvironment env) { - this.env = env; - } - - public TrafficHunterAgentProperty property() { - return env.load(); - } - - public TrafficHunterAgentProperty property(final InputStream is) { - return env.load(is); - } - - public void retransform(final Instrumentation inst) { - new Default() - .ignore(ignoreMatchPackage()) - .type(getSpringComponentMatcher()) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(TransactionAdvise.class).on(isMethod())) - ).installOn(inst); - } - - public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { - - final String agentName = property().name(); - - return new AgentMetadata( - UUIDGenerator.generate(agentName), - Environment.VERSION.version(), - agentName, - startTime, - new AtomicReference<>(status) - ); - } - - private Junction ignoreMatchPackage() { - return ElementMatchers.nameStartsWith("java.") - .or(ElementMatchers.nameStartsWith("sun.")) - .or(ElementMatchers.nameStartsWith("jdk.")); - } - - private ElementMatcher getSpringComponentMatcher() { - return isAnnotatedWith(named(AnnotationPath.SERVICE.getPath())) - .or(isAnnotatedWith(named(AnnotationPath.REST_CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.REPOSITORY.getPath()))); - } - - /** - *

- * Intercepts method execution to create a span for tracing. - * Handles span lifecycle, recording exceptions, and closing the scope. - *

- * - *

Note: Ensure the class has a public access modifier to avoid - * visibility issues when used with external components or frameworks like ByteBuddy. - * Using a private or package-private access modifier may result in runtime errors - * due to restricted access.

- * - * @see TraceManager - */ - public static class TransactionAdvise { - - public static final Tracer tracer = TraceManager.configure(new TraceExporter()); - - @OnMethodEnter - public static SpanScope enter(@Origin final String method) { - - Span currentSpan = Span.current(); - - Span span = tracer.spanBuilder(method) - .setParent(Context.current().with(currentSpan)) - .setAttribute("method.name", method) - .setStartTimestamp(Instant.now()) - .startSpan(); - - Scope scope = span.makeCurrent(); - - return new SpanScope(span, scope); - } - - @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { - - if (Objects.nonNull(throwable)) { - - String exception = throwable.getClass().getName() - + " " - + "(" - + throwable.getMessage() - + ")"; - - spanScope.span().recordException(throwable); - spanScope.span().setStatus(StatusCode.ERROR, exception); - } - - spanScope.span().end(Instant.now()); - spanScope.scope().close(); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java deleted file mode 100644 index 5d523642..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.context.execute; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; -import org.traffichunter.javaagent.bootstrap.engine.TrafficHunterAgentShutdownHook; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.event.listener.AgentStateEventListener; -import org.traffichunter.javaagent.event.object.AgentStateEvent; -import org.traffichunter.javaagent.event.store.AgentStateEventStore; - -/** - * The {@code TrafficHunterAgentExecutableContext} class represents the execution context - * for the TrafficHunter Agent. It manages the agent's state, event listeners, environment - * configuration, and shutdown operations. - * - *

Purpose:

- *
    - *
  • Maintains the current state of the agent using an atomic {@link AgentStatus}.
  • - *
  • Registers and manages {@link AgentStateEventListener} instances for state change notifications.
  • - *
  • Integrates a shutdown mechanism using {@link TrafficHunterAgentShutdownHook}.
  • - *
- * - *

Key Features:

- *
    - *
  • {@code init()} - Initializes the agent with the provided environment settings.
  • - *
  • {@code addAgentStateEventListener()} - Adds a listener to observe state changes.
  • - *
  • {@code removeAllAgentStateEventListeners()} - Removes all registered listeners.
  • - *
  • {@code close()} - Safely shuts down the agent using a dedicated shutdown thread.
  • - *
  • {@code setStatus()} - Atomically updates the agent's state and notifies listeners of the change.
  • - *
- * - *

Thread Safety:

- *
    - *
  • The {@code status} is managed using {@link AtomicReference}, ensuring thread-safe updates.
  • - *
  • Shutdown operations are protected by a {@link ReentrantLock} to prevent concurrent execution.
  • - *
  • {@link AtomicBoolean} is used to ensure the shutdown logic executes only once.
  • - *
- * - *

Limitations:

- *
    - *
  • State listeners are invoked sequentially; long-running listeners may delay others.
  • - *
  • Shutdown operations rely on an additional thread, which may cause delays during JVM termination.
  • - *
- * - * @see AgentExecutableContext - * @see AgentStatus - * @see TrafficHunterAgentShutdownHook - * @see ConfigurableContextInitializer - * @see AgentStateEventListener - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgentExecutableContext extends AgentStateEventStore implements AgentExecutableContext { - - private final ConfigurableEnvironment environment; - - private final AtomicReference status = new AtomicReference<>(AgentStatus.INITIALIZED); - - private final TrafficHunterAgentShutdownHook shutdownHook; - - private final ReentrantLock shutdownLock = new ReentrantLock(); - - private final AtomicBoolean isShutdown = new AtomicBoolean(false); - - public TrafficHunterAgentExecutableContext(final ConfigurableEnvironment environment, - final TrafficHunterAgentShutdownHook shutdownHook) { - this.environment = environment; - this.shutdownHook = shutdownHook; - } - - @Override - public void addAgentStateEventListener(final AgentStateEventListener listener) { - super.addAgentStateEventListener(listener); - } - - @Override - public void removeAgentStateEventListener(final AgentStateEventListener listener) { - super.removeAgentStateEventListener(listener); - } - - @Override - public void removeAllAgentStateEventListeners() { - super.removeAll(); - } - - @Override - public ConfigurableContextInitializer init() { - return new ConfigurableContextInitializer(environment); - } - - /** - * Safely shuts down the agent by executing the registered shutdown hooks. - *

Ensures that:

- *
    - *
  • The shutdown hook is executed only once.
  • - *
  • Concurrent shutdown attempts are prevented using a {@link ReentrantLock}.
  • - *
- * If the shutdown hook is not enabled, this method does nothing. - */ - @Override - public void close() { - - if(isStopped()) { - return; - } - - if(this.shutdownHook.isEnabledShutdownHook() && this.isShutdown.compareAndSet(false, true)) { - Thread shutdownHookThread; - shutdownLock.lock(); - try { - shutdownHookThread = new Thread(this.shutdownHook, "TrafficHunterAgentShutdownHook"); - shutdownHookThread.start(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - shutdownLock.unlock(); - } - } - } - - @Override - public ConfigurableEnvironment getEnvironment() { - return environment; - } - - @Override - public AgentStatus getStatus() { - return status.get(); - } - - @Override - public boolean isInit() { - return status.get() == AgentStatus.INITIALIZED; - } - - @Override - public boolean isRunning() { - return status.get() == AgentStatus.RUNNING; - } - - @Override - public boolean isStopped() { - return status.get() == AgentStatus.EXIT; - } - - @Override - public void setStatus(final AgentStatus newStatus) { - AgentStatus agentStatus; - - do { - agentStatus = status.get(); - } while (!status.compareAndSet(agentStatus, newStatus)); - - AgentStateEvent event = new AgentStateEvent(this, agentStatus, newStatus); - - notifyAgentStateChange(event); - } - - private void notifyAgentStateChange(final AgentStateEvent event) { - for(AgentStateEventListener listener : super.getListeners()) { - listener.onEvent(event); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java deleted file mode 100644 index 0908db02..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/ConfigurableEnvironment.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env; - -import java.io.InputStream; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; - -/** - * The {@code ConfigurableEnvironment} interface defines the contract for loading - * agent configuration properties. It supports loading configuration from - * default or provided input sources. - * - *

Features:

- *
    - *
  • Loads configuration properties into a {@link TrafficHunterAgentProperty} instance.
  • - *
  • Supports default and custom input streams for configuration sources.
  • - *
- * - * @see TrafficHunterAgentProperty - * @see YamlConfigurableEnvironment - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface ConfigurableEnvironment { - - TrafficHunterAgentProperty load(); - - TrafficHunterAgentProperty load(InputStream is); -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java deleted file mode 100644 index 7c923b76..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/Environment.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum Environment { - - DEFAULT_PATH("/env/agent-env.yml"), - VERSION("1.0.0"), - SYSTEM_PROFILE("traffichunter.config"), - ; - - private final String env; - - Environment(final String env) { - this.env = env; - } - - public String path() { - return env; - } - - public String version() { - return env; - } - - public String systemProfile() { - return env; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java deleted file mode 100644 index a19d6860..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/YamlConfigurableEnvironment.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml; - -import java.io.InputStream; -import java.util.concurrent.TimeUnit; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind.RelaxedBindingUtils; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.RootYamlProperty; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.commons.util.FileUtils; -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; -import org.traffichunter.javaagent.retry.backoff.policy.ExponentialBackOffPolicy; -import org.yaml.snakeyaml.LoaderOptions; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; - -/** - * The {@code YamlConfigurableEnvironment} class implements {@link ConfigurableEnvironment} - * to load configuration properties from a YAML file or input stream. - * - *

Features:

- *
    - *
  • Loads configuration properties from a YAML file or input stream.
  • - *
  • Parses YAML data into a {@link TrafficHunterAgentProperty} instance using - * custom relaxed binding rules.
  • - *
  • Supports a default configuration file or custom file paths.
  • - *
- * - * @see ConfigurableEnvironment - * @see TrafficHunterAgentProperty - * - * @author yungwang-o - * @version 1.0.0 - */ -public class YamlConfigurableEnvironment implements ConfigurableEnvironment { - - private static final String DEFAULT_CONFIG_FILE = "agent-env.yml"; - - private final Yaml yaml; - - private final String path; - - public YamlConfigurableEnvironment(final String path) { - final Constructor constructor = new Constructor(RootYamlProperty.class, new LoaderOptions()); - - constructor.setPropertyUtils(new RelaxedBindingUtils()); - - this.yaml = new Yaml(constructor); - this.path = path; - } - - @Override - public TrafficHunterAgentProperty load() { - final InputStream is = FileUtils.getFile(path); - - final RootYamlProperty root = yaml.load(is); - - return YamlParser.parse(root); - } - - @Override - public TrafficHunterAgentProperty load(final InputStream is) { - - final RootYamlProperty root = yaml.load(is); - - return YamlParser.parse(root); - } - - static class YamlParser { - - private static TrafficHunterAgentProperty parse(final RootYamlProperty root){ - - return TrafficHunterAgent.connect(root.getAgent().getServerUri()) - .name(root.getAgent().getName()) - .targetUri(root.getAgent().getTargetUri()) - .locationJar(root.getAgent().getJar()) - .scheduleInterval(root.getAgent().getInterval()) - .scheduleTimeUnit(TimeUnit.SECONDS) - .faultTolerant() - .retry(root.getAgent().getRetry().getMaxAttempt()) - .backOffPolicy(backOffPolicy(root.getAgent().getRetry().getBackoff())) - .complete(); - } - - private static BackOffPolicy backOffPolicy(BackOffSubProperty backOffSubProperty) { - if(backOffSubProperty == null) { - return ExponentialBackOffPolicy.DEFAULT; - } - - return new ExponentialBackOffPolicy( - backOffSubProperty.getIntervalMillis(), - backOffSubProperty.getMultiplier() - ); - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java deleted file mode 100644 index cfbfe221..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/bind/RelaxedBindingUtils.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml.bind; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.introspector.Property; -import org.yaml.snakeyaml.introspector.PropertyUtils; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RelaxedBindingUtils extends PropertyUtils { - - private static final Logger log = LoggerFactory.getLogger(RelaxedBindingUtils.class); - - @Override - public Property getProperty(final Class type, final String name) { - return super.getProperty(type, kebabToCamel(name)); - } - - private String kebabToCamel(final String kebab) { - final StringBuilder sb = new StringBuilder(); - - String[] split = kebab.split("-"); - - for(int i = 0; i < split.length; i++) { - if(i == 0) { - sb.append(split[i]); - continue; - } - sb.append(split[i].replaceFirst("^[a-z]", String.valueOf(split[i].charAt(0)).toUpperCase())); - } - - return sb.toString(); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java deleted file mode 100644 index 6acec047..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/RootYamlProperty.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root; - -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.AgentSubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RootYamlProperty { - - private AgentSubProperty agent; - - public RootYamlProperty() { - } - - public RootYamlProperty(final AgentSubProperty agent) { - this.agent = agent; - } - - public AgentSubProperty getAgent() { - return agent; - } - - public void setAgent(final AgentSubProperty agent) { - this.agent = agent; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java deleted file mode 100644 index 4e4702bb..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/AgentSubProperty.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent; - -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.RetrySubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentSubProperty { - - private String name; - private String jar; - private String serverUri; - private String targetUri; - private int interval; - private RetrySubProperty retry; - - public AgentSubProperty() { - } - - public AgentSubProperty(final String name, final String jar, final String serverUri, final String targetUri, - final int interval, - final RetrySubProperty retry) { - this.name = name; - this.jar = jar; - this.serverUri = serverUri; - this.targetUri = targetUri; - this.interval = interval; - this.retry = retry; - } - - public String getServerUri() { - return serverUri; - } - - public void setServerUri(final String serverUri) { - this.serverUri = serverUri; - } - - public String getTargetUri() { - return targetUri; - } - - public void setTargetUri(final String targetUri) { - this.targetUri = targetUri; - } - - public int getInterval() { - return interval; - } - - public void setInterval(final int interval) { - this.interval = interval; - } - - public RetrySubProperty getRetry() { - return retry; - } - - public void setRetry(final RetrySubProperty retry) { - this.retry = retry; - } - - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; - } - - public String getJar() { - return jar; - } - - public void setJar(final String jar) { - this.jar = jar; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java deleted file mode 100644 index 19cef295..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/RetrySubProperty.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry; - -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RetrySubProperty { - - private int maxAttempt; - private BackOffSubProperty backoff; - - public RetrySubProperty() { - } - - public RetrySubProperty(final int maxAttempt, final BackOffSubProperty backoff) { - this.maxAttempt = maxAttempt; - this.backoff = backoff; - } - - public int getMaxAttempt() { - return maxAttempt; - } - - public void setMaxAttempt(final int maxAttempt) { - this.maxAttempt = maxAttempt; - } - - public BackOffSubProperty getBackoff() { - return backoff; - } - - public void setBackoff(final BackOffSubProperty backoff) { - this.backoff = backoff; - } - - @Override - public String toString() { - return "RetrySubProperty{" + - "maxAttempt=" + maxAttempt + - ", backoff=" + backoff + - '}'; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java deleted file mode 100644 index 2bdb26d6..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.env.yaml.root.agent.retry.backoff; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class BackOffSubProperty { - - private long intervalMillis; - private int multiplier; - - public BackOffSubProperty() { - } - - public BackOffSubProperty(final long intervalMillis, final int multiplier) { - this.intervalMillis = intervalMillis; - this.multiplier = multiplier; - } - - public long getIntervalMillis() { - return intervalMillis; - } - - public void setIntervalMillis(final long intervalMillis) { - this.intervalMillis = intervalMillis; - } - - public int getMultiplier() { - return multiplier; - } - - public void setMultiplier(final int multiplier) { - this.multiplier = multiplier; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java deleted file mode 100644 index 1765afed..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.annotation; - -/** - * The {@code AnnotationPath} enum defines commonly used annotation paths - * for Spring-based applications, which are used for bytecode manipulation - * with ByteBuddy. - * - * @author yungwang-o - * @version 1.0.0 - */ -public enum AnnotationPath { - - TRANSACTIONAL("org.springframework.transaction.annotation.Transactional"), - SERVICE("org.springframework.stereotype.Service"), - REPOSITORY("org.springframework.stereotype.Repository"), - REST_CONTROLLER("org.springframework.web.bind.annotation.RestController"), - CONTROLLER("org.springframework.stereotype.Controller"), - ; - - private final String path; - - AnnotationPath(final String path) { - this.path = path; - } - - public String getPath() { - return path; - } - - public static boolean filter(final String path) { - return path.equals("join") || path.equals("wait") || path.equals("notify") || path.equals("notifyAll") || - path.equals("hashcode") || path.equals("equals") || path.equals("toString"); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java deleted file mode 100644 index bb93bd03..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class BootState { - - private static final Boolean STATE_NONE = false; - private static final Boolean STATE_STARTED = true; - - private final AtomicBoolean state = new AtomicBoolean(STATE_NONE); - - boolean getState() { - return state.get(); - } - - public boolean start() { - return state.compareAndSet(STATE_NONE, STATE_STARTED); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java deleted file mode 100644 index b1d51e3f..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.classloading; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class THAgentClassLoader extends ClassLoader { - - public THAgentClassLoader(final ClassLoader parent) { - super(parent); - } - - @Override - protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { - Class clazz = findLoadedClass(name); - - if(clazz == null) { - try { - clazz = findClass(name); - } catch (ClassNotFoundException ignored) { - } - - if(clazz == null) { - clazz = getParent().loadClass(name); - } - } - if(resolve) { - resolveClass(clazz); - } - return clazz; - } - } - - -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java deleted file mode 100644 index 452dbef3..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.locator; - -import java.io.File; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import org.traffichunter.javaagent.bootstrap.BootstrapMain; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentLocator { - - public static File getAgentJarFile() throws URISyntaxException { - - ProtectionDomain protectionDomain = BootstrapMain.class.getProtectionDomain(); - CodeSource codeSource = protectionDomain.getCodeSource(); - if(codeSource == null) { - throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain)); - } - - URL location = codeSource.getLocation(); - if(location == null) { - throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource)); - } - - final File agentJar = new File(location.toURI()); - if(agentJar.getName().endsWith(".jar")) { - throw new IllegalStateException("Agent is not a jar file: " + agentJar); - } - - return agentJar.getAbsoluteFile(); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java deleted file mode 100644 index c6f8522b..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/jvm/JVMSelector.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.jvm; - -import com.sun.tools.attach.AttachNotSupportedException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; -import java.io.IOException; -import java.util.Objects; -import java.util.logging.Logger; -import javax.management.remote.JMXServiceURL; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class JVMSelector { - - private static final Logger log = Logger.getLogger(JVMSelector.class.getName()); - - public static JMXServiceURL getVMXServiceUrl(final String targetJVMPath) { - - try { - final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); - - final VirtualMachine vm = VirtualMachine.attach(vmDescriptor.id().trim()); - - final String jmxUrl = vm.startLocalManagementAgent(); - - return new JMXServiceURL(jmxUrl); - } catch (AttachNotSupportedException | IOException e) { - log.warning("Not found jvm service url : " + e.getMessage()); - throw new RuntimeException(e); - } - } - - public static VirtualMachine getVM(final String targetJVMPath) { - - try { - final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); - - return VirtualMachine.attach(vmDescriptor.id().trim()); - - } catch (AttachNotSupportedException | IOException e) { - log.warning("Not found jvm service url : " + e.getMessage()); - throw new RuntimeException(e); - } - } - - private static VirtualMachineDescriptor getVirtualMachineDescriptor(final String targetJVMPath) { - - return VirtualMachine.list().stream() - .filter(vm -> Objects.equals(vm.displayName(), targetJVMPath)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No virtual machine found")); - } - - public static String displayName(final int selectNumber) { - return VirtualMachine.list().get(selectNumber - 1).displayName(); - } - - public static String JvmId(final int selectNumber) { - return VirtualMachine.list().get(selectNumber - 1).id(); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java deleted file mode 100644 index 7fd7ea42..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.lifecycle; - -import java.time.Duration; -import java.time.Instant; - -/** - * The {@code LifeCycle} abstract class defines a common structure for managing - * the lifecycle of an object, including its start time, end time, and uptime. - * - *

Features:

- *
    - *
  • Automatically initializes the start time when an instance is created.
  • - *
  • Provides abstract methods for retrieving the start time, end time, and uptime.
  • - *
  • Allows subclasses to implement specific behaviors for managing lifecycle information.
  • - *
- * - *

Usage:

- *
{@code
- * public class TaskLifeCycle extends LifeCycle {
- *
- *     @Override
- *     public Instant getStartTime() {
- *         return startTime;
- *     }
- *
- *     @Override
- *     public Instant getEndTime() {
- *         if (endTime == null) {
- *             endTime = Instant.now();
- *         }
- *         return endTime;
- *     }
- *
- *     @Override
- *     public Duration getUpTime() {
- *         return Duration.between(getStartTime(), getEndTime());
- *     }
- * }
- *
- * TaskLifeCycle task = new TaskLifeCycle();
- * System.out.println("Uptime: " + task.getUpTime().toSeconds() + " seconds");
- * }
- * - * @see Instant - * @see Duration - * - * @author yungwang-o - * @version 1.0.0 -*/ -public abstract class LifeCycle { - - protected final Instant startTime; - - protected Instant endTime; - - protected LifeCycle() { - this.startTime = Instant.now(); - } - - public abstract Instant getStartTime(); - - public abstract Instant getEndTime(); - - public abstract Double getUpTime(); -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java deleted file mode 100644 index 3834e172..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgent.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.property; - -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; -import org.traffichunter.javaagent.bootstrap.engine.property.child.FaultTolerantTrafficHunterAgent; - -/** - *

- * The {@code TrafficHunterAgent} class is responsible for holding and managing configuration - * details required to set up and execute traffic monitoring or management tasks. - * This class provides a fluent API for constructing and customizing the configuration - * properties, such as scheduling intervals, target URIs, and resource locations. - *

- * - *

Example Usage:

- *
{@code
- * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
- *     .name("MyAgent")
- *     .targetUri("localhost:8888")
- *     .locationJar("/path/to/agent.jar")
- *     .scheduleInterval(10)
- *     .scheduleTimeUnit(TimeUnit.MINUTES)
- *     .complete();
- * }
- * - *

Extensibility:

- *
    - *
  • The class can be extended with custom behavior, as seen in {@link FaultTolerantTrafficHunterAgent}.
  • - *
- * - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgent { - - private static final Logger log = Logger.getLogger(TrafficHunterAgent.class.getName()); - - protected int scheduleInterval; - - protected String targetUri; - - protected String jar; - - protected final String uri; - - protected TimeUnit timeUnit; - - protected String name; - - protected TrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { - this.name = trafficHunterAgent.name; - this.scheduleInterval = trafficHunterAgent.scheduleInterval; - this.targetUri = trafficHunterAgent.targetUri; - this.uri = trafficHunterAgent.uri; - this.timeUnit = trafficHunterAgent.timeUnit; - this.jar = trafficHunterAgent.jar; - } - - protected TrafficHunterAgent(final String uri) { - this.uri = uri; - } - - public static TrafficHunterAgent connect(final String serverUrl) { - return new TrafficHunterAgent(serverUrl); - } - - public FaultTolerantTrafficHunterAgent faultTolerant() { - return new FaultTolerantTrafficHunterAgent(this); - } - - public TrafficHunterAgent name(final String name) { - this.name = name; - return this; - } - - public TrafficHunterAgent targetUri(final String targetUri) { - this.targetUri = targetUri; - return this; - } - - public TrafficHunterAgent locationJar(final String jar) { - this.jar = jar; - return this; - } - - public TrafficHunterAgent scheduleInterval(final int scheduleInterval) { - this.scheduleInterval = scheduleInterval; - return this; - } - - public TrafficHunterAgent scheduleTimeUnit(final TimeUnit timeUnit) { - this.timeUnit = timeUnit; - return this; - } - - public TrafficHunterAgentProperty complete() { - return new TrafficHunterAgentProperty(name, targetUri, jar, scheduleInterval, uri, timeUnit); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java deleted file mode 100644 index 130460ae..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/TrafficHunterAgentProperty.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.property; - -import java.util.concurrent.TimeUnit; -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TrafficHunterAgentProperty( - String name, - String targetUri, - String jar, - int scheduleInterval, - String serverUri, - TimeUnit timeUnit, - int maxAttempt, - BackOffPolicy backOffPolicy -) { - - public TrafficHunterAgentProperty(final String name, - final String targetUri, - final String jar, - final int scheduleInterval, - final String serverUri, - final TimeUnit timeUnit, - final int maxAttempt, - final BackOffPolicy backOffPolicy) { - - this.name = name; - this.targetUri = targetUri; - this.jar = jar; - this.scheduleInterval = scheduleInterval; - this.serverUri = serverUri; - this.timeUnit = timeUnit; - this.maxAttempt = maxAttempt; - this.backOffPolicy = backOffPolicy; - } - - public TrafficHunterAgentProperty(final String name, - final String targetUri, - final String jar, - final int scheduleInterval, - final String serverUri, - final TimeUnit timeUnit) { - - this(name, targetUri, jar, scheduleInterval, serverUri, timeUnit, 0, null); - } - - @Override - public String toString() { - return "TrafficHunterAgentProperty{" + - "name='" + name + '\'' + - ", targetUri='" + targetUri + '\'' + - ", jar=" + jar + - ", scheduleInterval=" + scheduleInterval + - ", serverUri=" + serverUri + - ", timeUnit=" + timeUnit + - ", maxAttempt=" + maxAttempt + - ", backOffPolicy=" + backOffPolicy.toString() + - '}'; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java deleted file mode 100644 index d2c8afc8..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/property/child/FaultTolerantTrafficHunterAgent.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.property.child; - -import java.util.logging.Logger; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgent; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; - -/** - * The {@code FaultTolerantTrafficHunterAgent} class extends the functionality of - * {@link TrafficHunterAgent} by adding fault-tolerant capabilities, such as retry logic - * and back-off policies. This class is designed to handle scenarios where failures - * may occur and ensures that the agent can recover gracefully. - * - *

Example Usage:

- *
{@code
- * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
- *     .name("ResilientAgent")
- *     .targetUri("localhost:8888")
- *     .locationJar("/path/to/agent.jar")
- *     .scheduleInterval(10)
- *     .scheduleTimeUnit(TimeUnit.MINUTES)
- *     .faultTolerant()
- *     .retry(5) // Set maximum retry attempts to 5
- *     .backOffPolicy(new ExponentialBackOffPolicy()) // Use exponential back-off policy
- *     .complete();
- * }
- * - * @see TrafficHunterAgent - * @see BackOffPolicy - * @author yungwang-o - * @version 1.0.0 - */ -public class FaultTolerantTrafficHunterAgent extends TrafficHunterAgent { - - private static final Logger log = Logger.getLogger(FaultTolerantTrafficHunterAgent.class.getName()); - private BackOffPolicy backOffPolicy; - private int maxAttempt; - - public FaultTolerantTrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { - super(trafficHunterAgent); - } - - public FaultTolerantTrafficHunterAgent retry(final int maxAttempt) { - this.maxAttempt = maxAttempt; - return this; - } - - public FaultTolerantTrafficHunterAgent backOffPolicy(final BackOffPolicy backOffPolicy) { - this.backOffPolicy = backOffPolicy; - return this; - } - - @Override - public TrafficHunterAgentProperty complete() { - return new TrafficHunterAgentProperty( - this.name, - this.targetUri, - this.jar, - this.scheduleInterval, - this.uri, - this.timeUnit, - maxAttempt, - backOffPolicy - ); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java deleted file mode 100644 index 40f9ccd3..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender; - -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; - -/** - * The {@code MetricSender} interface defines the contract for sending metrics. - * Implementations of this interface are responsible for serializing and sending - * specific types of metrics using a given communication client. - * - * @see AgentTransactionMetricSender - * @see AgentSystemMetricSender - */ -public interface MetricSender { - - void toSend(AgentMetadata metadata); -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java deleted file mode 100644 index 532cb0b4..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java +++ /dev/null @@ -1,198 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.manager; - -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryConfig; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.commons.util.AgentUtil; -import org.traffichunter.javaagent.retry.RetryHelper; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.metadata.Metadata; - -/** - * The {@code MetricSendSessionManager} class manages the session for sending - * transaction and system metrics from the TrafficHunter Agent to a server. - * It handles WebSocket connections, retries, scheduling, and lifecycle management - * of the metric-sending operations. - * - *

Purpose:

- *
    - *
  • Manages WebSocket connections for real-time metric transmission.
  • - *
  • Schedules periodic system metric transmissions using a {@link ScheduledExecutorService}.
  • - *
  • Retries connection attempts in case of failures with a configurable back-off policy.
  • - *
  • Provides lifecycle management for starting and stopping the session.
  • - *
- * - *

Key Features:

- *
    - *
  • {@code run()} - Starts the metric sending session, ensuring retries and scheduling are properly configured.
  • - *
  • {@code close()} - Safely shuts down the session, releasing all resources.
  • - *
  • Integrates with {@link Retry} to handle WebSocket reconnections in case of failures.
  • - *
- * - *

Thread Management:

- *
    - *
  • Uses a {@link ScheduledExecutorService} for scheduling periodic system metrics.
  • - *
  • Uses a {@link ExecutorService} for running transaction metric transmissions.
  • - *
  • Both executors are safely shut down during the {@code close()} method.
  • - *
- * - *

Limitations:

- *
    - *
  • If the agent's context status is already {@code RUNNING}, the session cannot be restarted without stopping it first.
  • - *
  • Retries only handle specific exceptions, as configured in the {@link RetryHelper}.
  • - *
- * - * @see Retry - * @see ScheduledExecutorService - * @see MetricWebSocketClient - * @see TrafficHunterAgentProperty - * @see AgentExecutableContext - * @see AgentMetadata - * @see RetryHelper - * @author yungwang-o - * @version 1.0.0 - */ -public class MetricSendSessionManager { - - private static final Logger log = LoggerFactory.getLogger(MetricSendSessionManager.class); - - private final TrafficHunterAgentProperty property; - - private final AgentTransactionMetricSender transactionMetricSender; - - private final AgentSystemMetricSender systemMetricSender; - - private final ScheduledExecutorService schedule; - - private final ExecutorService executor; - - private final AgentExecutableContext context; - - private final AgentMetadata metadata; - - private final MetricWebSocketClient client; - - public MetricSendSessionManager(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.client = initializeWebsocket(property, metadata); - this.client.connect(); - this.metadata = metadata; - this.context = context; - this.property = property; - this.transactionMetricSender = new AgentTransactionMetricSender(client); - this.systemMetricSender = new AgentSystemMetricSender(client, property); - this.schedule = Executors.newSingleThreadScheduledExecutor(getThreadFactory("TransactionSystemInfoMetricSender")); - this.executor = Executors.newVirtualThreadPerTaskExecutor(); - } - - public void run() { - - if(context.isStopped()) { - log.error("MetricSendSessionManager is stopped"); - return; - } - - if(context.isRunning()) { - log.error("MetricSendSessionManager is already running"); - return; - } - - log.info("start Metric send!!"); - - final RetryHelper retryHelper = RetryHelper.builder() - .backOffPolicy(property.backOffPolicy()) - .isCheck(true) - .retryName("websocket retry") - .maxAttempts(property.maxAttempt()) - .retryPredicate(throwable -> throwable instanceof IllegalStateException) - .build(); - - RetryConfig retryConfig = retryHelper.configureRetry(); - - Retry retry = Retry.of(retryHelper.getRetryName(), retryConfig); - - retry.getEventPublisher() - .onRetry(event -> { - client.reconnect(); - log.info("{} retry {} attempts...", event.getName(), event.getNumberOfRetryAttempts()); - }); - - context.setStatus(AgentStatus.RUNNING); - - executor.execute(Retry.decorateRunnable(retry, () -> transactionMetricSender.toSend(metadata))); - - schedule.scheduleWithFixedDelay(Retry.decorateRunnable(retry, () -> systemMetricSender.toSend(metadata)), - 0, - property.scheduleInterval(), - property.timeUnit() - ); - } - - public void close() { - log.info("closing MetricSendSessionManager..."); - context.setStatus(AgentStatus.EXIT); - client.close(); - executor.shutdown(); - schedule.shutdown(); - } - - private static MetricWebSocketClient initializeWebsocket(final TrafficHunterAgentProperty property, - final AgentMetadata metadata) { - return new MetricWebSocketClient( - AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), - Metadata.builder() - .agentId(metadata.agentId()) - .agentVersion(metadata.agentVersion()) - .agentName(metadata.agentName()) - .startTime(metadata.startTime()) - .status(metadata.status().get()) - .build() - ); - } - - private ThreadFactory getThreadFactory(final String threadName) { - return r -> { - Thread thread = new Thread(r); - thread.setDaemon(true); - thread.setName(threadName); - - return thread; - }; - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java deleted file mode 100644 index eb535e22..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.collect.MetricCollectSupport; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; -import org.traffichunter.javaagent.commons.dto.systeminfo.SystemInfo; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - * The {@code AgentSystemMetricSender} class is responsible for sending system metrics - * to the server via a WebSocket connection. - * - *

Features:

- *
    - *
  • Collects system metric data from the local environment.
  • - *
  • Wraps the system data with metadata and sends it in a compressed format.
  • - *
  • Uses {@link MetricCollectSupport} for metric collection.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see MetricCollectSupport - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentSystemMetricSender implements MetricSender { - - private static final Logger log = LoggerFactory.getLogger(AgentSystemMetricSender.class); - - private final MetricWebSocketClient client; - - private final MetricCollectSupport metricCollectSupport; - - public AgentSystemMetricSender(final MetricWebSocketClient client, - final TrafficHunterAgentProperty property) { - this.client = client; - this.metricCollectSupport = new MetricCollectSupport(property); - } - - @Override - public void toSend(final AgentMetadata metadata) { - final SystemInfo systemInfo = metricCollectSupport.collect(); - - final MetadataWrapper wrapper = MetadataWrapper.create(metadata, systemInfo); - - client.compressToSend(wrapper, MetricType.SYSTEM_METRIC); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java deleted file mode 100644 index 1a39c5c2..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; -import org.traffichunter.javaagent.trace.dto.TraceInfo; -import org.traffichunter.javaagent.trace.queue.TraceQueue; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - *

- * The {@code AgentTransactionMetricSender} class is responsible for sending transaction metrics - * to the server via a WebSocket connection. - *

- * - *

Features:

- *
    - *
  • Continuously retrieves transaction data from a synchronized queue.
  • - *
  • Wraps the transaction data with metadata and sends it in a compressed format.
  • - *
  • Relies on {@link TraceQueue} for thread-safe access to transaction data.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see TraceQueue - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentTransactionMetricSender implements MetricSender { - - public static final Logger log = LoggerFactory.getLogger(AgentTransactionMetricSender.class); - - private final MetricWebSocketClient client; - - public AgentTransactionMetricSender(final MetricWebSocketClient client) { - this.client = client; - } - - @Override - public void toSend(final AgentMetadata metadata) { - - while (!Thread.currentThread().isInterrupted()) { - - try { - - TraceInfo trInfo = TraceQueue.INSTANCE.poll(); - - MetadataWrapper wrapper = MetadataWrapper.create(metadata, trInfo); - - client.compressToSend(wrapper, MetricType.TRANSACTION_METRIC); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } catch (IllegalStateException e) { - log.error("exception while sending transaction metric = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java deleted file mode 100644 index 3b65d6cb..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/AgentMetadata.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.metadata; - -import java.time.Instant; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.event.listener.AgentStateEventListener; -import org.traffichunter.javaagent.event.object.AgentStateEvent; - -/** - * The {@code AgentMetadata} record represents metadata for an agent and acts as an - * {@link AgentStateEventListener} to respond to state change events. - * - *

Features:

- *
    - *
  • Stores immutable metadata fields such as agent ID, version, name, and start time.
  • - *
  • Maintains a mutable {@link AtomicReference} for the agent's current status.
  • - *
  • Implements {@link AgentStateEventListener} to react to state change events.
  • - *
  • Overrides {@code equals} and {@code hashCode} to handle mutable status appropriately.
  • - *
- * - * @see AgentStateEventListener - * @see AgentStateEvent - * @see AgentStatus - * @see AtomicReference - * - * @author yungwang-o - * @version 1.0.0 - */ -public record AgentMetadata( - String agentId, - String agentVersion, - String agentName, - Instant startTime, - AtomicReference status -) implements AgentStateEventListener { - - @Override - public void onEvent(final AgentStateEvent event) { - this.setStatus(event.getAfterStatus()); - } - - private void setStatus(final AgentStatus status) { - this.status.set(status); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AgentMetadata that = (AgentMetadata) o; - return Objects.equals(agentId, that.agentId) && - Objects.equals(agentVersion, that.agentVersion) && - Objects.equals(agentName, that.agentName) && - Objects.equals(startTime, that.startTime) && - Objects.equals(status.get(), that.status.get()); - } - - @Override - public int hashCode() { - return Objects.hash(agentId, agentVersion, agentName, startTime, status.get()); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java b/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java deleted file mode 100644 index 64b9bd93..00000000 --- a/java-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/metadata/MetadataWrapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.metadata; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record MetadataWrapper(AgentMetadata metadata, D data) { - - public static MetadataWrapper create(AgentMetadata metadata, D data) { - return new MetadataWrapper<>(metadata, data); - } -} diff --git a/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt b/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt deleted file mode 100644 index adb4b4ec..00000000 --- a/java-agent/java-agent-bootstrap/src/main/resources/agent-banner.txt +++ /dev/null @@ -1,11 +0,0 @@ -_____ ___________________ ______ _____ -__ /_____________ ___ __/__ __/__(_)______ ___ /_____ __________ /_____________ -_ __/_ ___/ __ `/_ /_ __ /_ __ /_ ___/ __ __ \ / / /_ __ \ __/ _ \_ ___/ -/ /_ _ / / /_/ /_ __/ _ __/ _ / / /__ _ / / / /_/ /_ / / / /_ / __/ / -\__/ /_/ \__,_/ /_/ /_/ /_/ \___/ /_/ /_/\__,_/ /_/ /_/\__/ \___//_/ - -:: Traffic Hunter ${version} Ver :: -:: Made by ygo :: -:: Running on Java ${java.version} :: -:: JDK Information ${jdk} ${java.specification} :: -:: Started on ${time} :: \ No newline at end of file diff --git a/java-agent/java-agent-commons/build.gradle b/java-agent/java-agent-commons/build.gradle deleted file mode 100644 index c2688fc2..00000000 --- a/java-agent/java-agent-commons/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.commons' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-commons/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-commons/gradlew.bat b/java-agent/java-agent-commons/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-commons/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java deleted file mode 100644 index 92c59078..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/dbcp/HikariDbcpInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.dbcp; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record HikariDbcpInfo( - // Connection Status - int activeConnections, // Number of active connections currently in use - int idleConnections, // Number of idle connections in the pool - int totalConnections, // Total number of connections in the pool - int threadsAwaitingConnection // Number of threads waiting for a connection -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java deleted file mode 100644 index 119ed493..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/SystemInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo; - -import java.time.Instant; -import org.traffichunter.javaagent.commons.dto.dbcp.HikariDbcpInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.cpu.CpuStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.gc.GarbageCollectionStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.memory.MemoryStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.runtime.RuntimeStatusInfo; -import org.traffichunter.javaagent.commons.dto.systeminfo.thread.ThreadStatusInfo; -import org.traffichunter.javaagent.commons.dto.web.tomcat.TomcatWebServerInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record SystemInfo( - Instant time, - MemoryStatusInfo memoryStatusInfo, - ThreadStatusInfo threadStatusInfo, - CpuStatusInfo cpuStatusInfo, - GarbageCollectionStatusInfo garbageCollectionStatusInfo, - RuntimeStatusInfo runtimeStatusInfo, - TomcatWebServerInfo tomcatWebServerInfo, - HikariDbcpInfo hikariDbcpInfo -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java deleted file mode 100644 index 3ea992b9..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/cpu/CpuStatusInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.cpu; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record CpuStatusInfo(double systemCpuLoad, double processCpuLoad, long availableProcessors) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java deleted file mode 100644 index 06ac48c1..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/GarbageCollectionStatusInfo.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.gc; - -import java.util.List; -import org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections.GarbageCollectionTime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record GarbageCollectionStatusInfo(List garbageCollectionTimes) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java deleted file mode 100644 index a55ccac0..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/gc/collections/GarbageCollectionTime.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.gc.collections; - -import java.lang.management.GarbageCollectorMXBean; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record GarbageCollectionTime( - long getCollectionCount, - long getCollectionTime -) { - - public GarbageCollectionTime(final GarbageCollectorMXBean mxBean) { - this(mxBean.getCollectionCount(), mxBean.getCollectionTime()); - } -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java deleted file mode 100644 index 58b13550..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/memory/MemoryStatusInfo.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.memory; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record MemoryStatusInfo(MemoryUsage heapMemoryUsage, MemoryUsage nonHeapMemoryUsage) { - - public record MemoryUsage(long init, long used, long committed, long max) {} -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java deleted file mode 100644 index 69dd8be1..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/runtime/RuntimeStatusInfo.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.runtime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record RuntimeStatusInfo( - long getStartTime, - long getUpTime, - String getVmName, - String getVmVersion -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java deleted file mode 100644 index 6fc95de0..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/systeminfo/thread/ThreadStatusInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.systeminfo.thread; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record ThreadStatusInfo( - int threadCount, - int getPeekThreadCount, - long getTotalStartThreadCount -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java deleted file mode 100644 index bdd53433..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/TomcatWebServerInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.web.tomcat; - -import org.traffichunter.javaagent.commons.dto.web.tomcat.request.TomcatRequestInfo; -import org.traffichunter.javaagent.commons.dto.web.tomcat.thread.TomcatThreadPoolInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatWebServerInfo( - TomcatThreadPoolInfo tomcatThreadPoolInfo, - TomcatRequestInfo tomcatRequestInfo -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java deleted file mode 100644 index 4e434855..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/request/TomcatRequestInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.web.tomcat.request; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatRequestInfo( - long requestCount, - long bytesReceived, - long bytesSent, - long processingTime, - long errorCount -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java deleted file mode 100644 index fae0d023..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/dto/web/tomcat/thread/TomcatThreadPoolInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.dto.web.tomcat.thread; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatThreadPoolInfo( - int maxThreads, - int currentThreads, - int currentThreadsBusy -) { -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java deleted file mode 100644 index a514ab8f..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/status/AgentStatus.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.status; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum AgentStatus { - INITIALIZED, - RUNNING, - EXIT -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java deleted file mode 100644 index fd781fc2..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/AgentUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.util; - -import java.net.URI; - -/** - * The {@code AgentUtil} enum provides utility methods for constructing - * WebSocket and HTTP URLs for the TrafficHunter Agent. It also includes - * methods for validating and formatting server addresses. - * - * @author yungwang-o - * @version 1.0.0 -*/ -public enum AgentUtil { - WEBSOCKET_URL("ws://%s/traffic-hunter/tx"), - HTTP_URL("http://%s/traffic-hunter"), - ; - - private static final String pattern = "^(localhost|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):\\d{1,5}$"; - - private final String url; - - AgentUtil(final String url) { - this.url = url; - } - - public static boolean isAddr(final String serverUrl) { - return serverUrl.matches(pattern); - } - - public String getUrl(final String serverUrl) { - return String.format(url, serverUrl); - } - - public URI getUri(final String serverUrl) { - return URI.create(String.format(url, serverUrl)); - } -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java deleted file mode 100644 index 60389eef..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/FileUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.util; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.util.logging.Logger; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class FileUtils { - - private static final Logger log = Logger.getLogger(FileUtils.class.getName()); - - public static FileInputStream getFile(final String path) { - try { - return new FileInputStream(path); - } catch (FileNotFoundException e) { - log.severe("Could not open file " + path + " " + e); - throw new RuntimeException(e); - } - } - - -} diff --git a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java b/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java deleted file mode 100644 index f2cbee9d..00000000 --- a/java-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/util/UUIDGenerator.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.commons.util; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.UUID; -import java.util.logging.Logger; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class UUIDGenerator { - - private static final Logger log = Logger.getLogger(UUIDGenerator.class.getName()); - - public static String generate(final String agentName) { - Path path = Paths.get(getPath(agentName)); - - if(!Files.exists(path.getParent())) { - try { - Files.createDirectories(path.getParent()); - } catch (IOException e) { - log.severe("Failed to create directory " + e.getMessage()); - throw new RuntimeException(e); - } - } - - if(Files.exists(path)) { - try { - return Files.readString(path).trim(); - } catch (IOException e) { - log.severe("Failed to read file " + e.getMessage()); - throw new RuntimeException(e); - } - } - - final String uuid = UUID.randomUUID().toString(); - - try { - Files.writeString(path, uuid); - return uuid; - } catch (IOException e) { - log.severe("Failed to write file " + e.getMessage()); - throw new RuntimeException(e); - } - } - - private static String getPath(final String agentName) { - return System.getProperty("user.home") - + "/traffic-hunter" - + "/key" - + "/" - + agentName - + "_" - + "agent_id.txt"; - } -} diff --git a/java-agent/java-agent-event/build.gradle b/java-agent/java-agent-event/build.gradle deleted file mode 100644 index 77a6a43b..00000000 --- a/java-agent/java-agent-event/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.event' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-agent:java-agent-commons') -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-event/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-event/gradlew.bat b/java-agent/java-agent-event/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-event/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java deleted file mode 100644 index 3dc955eb..00000000 --- a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/AgentEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.event; - -import java.time.Instant; -import java.util.EventObject; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public abstract class AgentEvent extends EventObject { - - private final long timestamp; - - public AgentEvent(final Object source) { - super(source); - this.timestamp = System.currentTimeMillis(); - } - - public AgentEvent(final Object source, final Instant instant) { - super(source); - this.timestamp = instant.toEpochMilli(); - } - - public final long getTimestamp() { - return this.timestamp; - } - - public Object getSource() { - return super.getSource(); - } -} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java deleted file mode 100644 index 9a4d50d7..00000000 --- a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/context/AgentContextStateEventHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.event.context; - -import org.traffichunter.javaagent.event.listener.AgentStateEventListener; - -/** - * The {@code AgentContextStateEventHandler} interface defines methods for managing - * {@link AgentStateEventListener} instances. It allows adding, removing, and clearing - * listeners for handling agent state change events. - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentContextStateEventHandler { - - void addAgentStateEventListener(final AgentStateEventListener listener); - - void removeAgentStateEventListener(final AgentStateEventListener listener); - - void removeAllAgentStateEventListeners(); -} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java deleted file mode 100644 index a6a74855..00000000 --- a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/listener/AgentStateEventListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.event.listener; - -import java.util.EventListener; -import org.traffichunter.javaagent.event.object.AgentStateEvent; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentStateEventListener extends EventListener { - - void onEvent(AgentStateEvent event); -} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java deleted file mode 100644 index 8d97e0fa..00000000 --- a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/object/AgentStateEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.event.object; - -import java.time.Instant; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.event.AgentEvent; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentStateEvent extends AgentEvent { - - private final AgentStatus beforeStatus; - - private final AgentStatus afterStatus; - - public AgentStateEvent(final Object source, - final AgentStatus beforeStatus, - final AgentStatus afterStatus) { - - super(source); - this.beforeStatus = beforeStatus; - this.afterStatus = afterStatus; - } - - public AgentStateEvent(final Object source, - final Instant instant, - final AgentStatus beforeStatus, - final AgentStatus afterStatus) { - - super(source, instant); - this.beforeStatus = beforeStatus; - this.afterStatus = afterStatus; - } - - public AgentStatus getBeforeStatus() { - return this.beforeStatus; - } - - public AgentStatus getAfterStatus() { - return this.afterStatus; - } -} diff --git a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java b/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java deleted file mode 100644 index 9ac280b6..00000000 --- a/java-agent/java-agent-event/src/main/java/org/traffichunter/javaagent/event/store/AgentStateEventStore.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.event.store; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import org.traffichunter.javaagent.event.listener.AgentStateEventListener; - -/** - * The {@code AgentStateEventStore} class manages a collection of {@link AgentStateEventListener} instances. - * It provides methods to add, remove, and retrieve listeners, enabling event-driven state management - * for the TrafficHunter Agent. - * - *

Purpose:

- *
    - *
  • Stores event listeners for agent state changes.
  • - *
  • Optimized for frequent reads and infrequent writes using {@link CopyOnWriteArrayList}.
  • - *
- * - * @see AgentStateEventListener - * @see CopyOnWriteArrayList - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentStateEventStore { - - private final List listeners = new CopyOnWriteArrayList<>(); - - protected void addAgentStateEventListener(final AgentStateEventListener listener) { - listeners.add(listener); - } - - protected void removeAgentStateEventListener(final AgentStateEventListener listener) { - if(listeners.isEmpty()) { - throw new IllegalArgumentException("listener is empty"); - } - - listeners.remove(listener); - } - - public List getListeners() { - return listeners; - } - - protected void removeAll() { - listeners.clear(); - } - - public int listenerSize() { - return listeners.size(); - } -} diff --git a/java-agent/java-agent-retry/build.gradle b/java-agent/java-agent-retry/build.gradle deleted file mode 100644 index c31598f4..00000000 --- a/java-agent/java-agent-retry/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.retry' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-retry/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-retry/gradlew.bat b/java-agent/java-agent-retry/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-retry/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java deleted file mode 100644 index e8f96491..00000000 --- a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/RetryHelper.java +++ /dev/null @@ -1,169 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.retry; - -import io.github.resilience4j.core.IntervalFunction; -import io.github.resilience4j.retry.RetryConfig; -import java.util.function.Predicate; -import java.util.logging.Logger; -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; - -/** - * The {@code RetryHelper} class simplifies the configuration of retry logic, - * including backoff policies, maximum attempts, and exception handling. - * - *

Features:

- *
    - *
  • Provides a fluent {@code Builder} for configuring retry parameters.
  • - *
  • Supports customizable backoff policies via {@link BackOffPolicy}.
  • - *
  • Configures exception-based retry logic with a {@link Predicate}.
  • - *
  • Generates a {@link RetryConfig} compatible with retry libraries.
  • - *
- * - *

Example:

- *
{@code
- * RetryHelper retryHelper = RetryHelper.builder()
- *     .backOffPolicy(new BackOffPolicy(1000, 2))
- *     .maxAttempts(5)
- *     .retryPredicate(e -> e instanceof IllegalStateException)
- *     .retryName("SampleRetry")
- *     .isCheck(true)
- *     .build();
- *
- * RetryConfig retryConfig = retryHelper.configureRetry();
- * }
- * - *

Thread Safety:

- *
    - *
  • This class is immutable and thread-safe once built.
  • - *
- * - * @see RetryConfig - * @see BackOffPolicy - * - * @author yungwang-o - * @version 1.0.0 - */ -public class RetryHelper { - - private static final Logger log = Logger.getLogger(RetryHelper.class.getName()); - - private final BackOffPolicy backOffPolicy; - - private final int maxAttempts; - - private final Predicate retryPredicate; - - private final String retryName; - - private final boolean isCheck; - - private RetryHelper(final Builder builder) { - this.backOffPolicy = builder.backOffPolicy; - this.maxAttempts = builder.maxAttempts; - this.retryPredicate = builder.retryPredicate; - this.retryName = builder.retryName; - this.isCheck = builder.isCheck; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private BackOffPolicy backOffPolicy; - - private int maxAttempts; - - private Predicate retryPredicate; - - private String retryName; - - private boolean isCheck; - - public Builder() { - } - - public Builder backOffPolicy(final BackOffPolicy backOffPolicy) { - this.backOffPolicy = backOffPolicy; - return this; - } - - public Builder maxAttempts(final int maxAttempts) { - this.maxAttempts = maxAttempts; - return this; - } - - public Builder retryPredicate(final Predicate retryPredicate) { - this.retryPredicate = retryPredicate; - return this; - } - - public Builder retryName(final String retryName) { - this.retryName = retryName; - return this; - } - - public Builder isCheck(final boolean isCheck) { - this.isCheck = isCheck; - return this; - } - - public RetryHelper build() { - return new RetryHelper(this); - } - } - - public BackOffPolicy getBackOffPolicy() { - return backOffPolicy; - } - - public int getMaxAttempts() { - return maxAttempts; - } - - public Predicate getRetryPredicate() { - return retryPredicate; - } - - public String getRetryName() { - return retryName; - } - - public boolean isCheck() { - return isCheck; - } - - public RetryConfig configureRetry() { - return RetryConfig.custom() - .maxAttempts(maxAttempts) - .retryOnException(retryPredicate) - .failAfterMaxAttempts(isCheck) - .intervalFunction(IntervalFunction.ofExponentialRandomBackoff( - backOffPolicy.getIntervalMillis(), - backOffPolicy.getMultiplier() - )).build(); - } -} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java deleted file mode 100644 index 14916566..00000000 --- a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/BackOffPolicy.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.retry.backoff; - -/** - * The {@code BackOffPolicy} class serves as the base class for defining backoff strategies - * used in retry mechanisms. It provides common properties such as the interval and multiplier, - * which can be extended for specific backoff behaviors. - * - *

Subclasses:

- *
    - *
  • {@link ExponentialBackOffPolicy}: Implements exponential backoff strategy.
  • - *
  • {@link FixedBackOffPolicy}: Implements fixed interval backoff strategy.
  • - *
- * - * @author yungwang-o - * @version 1.0.0 - */ -public class BackOffPolicy { - - private final long intervalMillis; - private final int multiplier; - - public BackOffPolicy(final long intervalMillis, final int multiplier) { - this.intervalMillis = intervalMillis; - this.multiplier = multiplier; - } - - public long getIntervalMillis() { - return intervalMillis; - } - - public int getMultiplier() { - return multiplier; - } - - @Override - public String toString() { - return "BackOffPolicy{" + - "intervalMillis=" + intervalMillis + - ", multiplier=" + multiplier + - '}'; - } -} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java deleted file mode 100644 index 586bbb00..00000000 --- a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/ExponentialBackOffPolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.retry.backoff.policy; - -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class ExponentialBackOffPolicy extends BackOffPolicy { - - public static final ExponentialBackOffPolicy DEFAULT = new ExponentialBackOffPolicy(1000, 2); - - public ExponentialBackOffPolicy(final long intervalMillis, final int multiplier) { - super(intervalMillis, multiplier); - } -} diff --git a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java b/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java deleted file mode 100644 index 8c3597d4..00000000 --- a/java-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/backoff/policy/FixedBackOffPolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.retry.backoff.policy; - -import org.traffichunter.javaagent.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class FixedBackOffPolicy extends BackOffPolicy { - - public static final FixedBackOffPolicy DEFAULT = new FixedBackOffPolicy(1000); - - public FixedBackOffPolicy(final long intervalMillis) { - super(intervalMillis, 1); - } -} diff --git a/java-agent/java-agent-trace/build.gradle b/java-agent/java-agent-trace/build.gradle deleted file mode 100644 index e45495c6..00000000 --- a/java-agent/java-agent-trace/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.trace' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-agent:java-agent-commons') - - implementation 'io.opentelemetry:opentelemetry-api:1.45.0' - implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-trace/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-trace/gradlew.bat b/java-agent/java-agent-trace/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-trace/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java deleted file mode 100644 index c180b4dd..00000000 --- a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.trace.dto; - -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.time.Duration; -import java.time.Instant; -import java.util.Objects; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TraceInfo( - - String name, - - String traceId, - - String parentSpanId, - - String spanId, - - Instant startTime, - - Instant endTime, - - long duration, - - String exception, - - boolean ended -) { - - public static TraceInfo translate(final SpanData spanData) { - - Instant startTime = Instant.EPOCH.plusNanos(spanData.getStartEpochNanos()); - - Instant endTime = Instant.EPOCH.plusNanos(spanData.getEndEpochNanos()); - - Duration between = Duration.between(startTime, endTime); - - return new TraceInfo( - spanData.getName(), - spanData.getTraceId(), - spanData.getParentSpanId(), - spanData.getSpanId(), - startTime, - endTime, - between.toMillis(), - getException(spanData), - spanData.hasEnded() - ); - } - - private static String getException(final SpanData spanData) { - if(Objects.equals(spanData.getStatus().getStatusCode(), StatusCode.ERROR)) { - return spanData.getStatus().getDescription(); - } - - return "success"; - } -} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java deleted file mode 100644 index 21e41c86..00000000 --- a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.trace.exporter; - -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.util.Collection; -import java.util.logging.Logger; -import org.traffichunter.javaagent.trace.dto.TraceInfo; -import org.traffichunter.javaagent.trace.queue.TraceQueue; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TraceExporter implements SpanExporter { - - private static final Logger log = Logger.getLogger(TraceExporter.class.getName()); - - private volatile boolean isShutdown = false; - - @Override - public CompletableResultCode export(final Collection spans) { - - if(isShutdown) { - return CompletableResultCode.ofFailure(); - } - - try { - spans.stream() - .map(TraceInfo::translate) - .forEach(TraceQueue.INSTANCE::add); - - return CompletableResultCode.ofSuccess(); - } catch (RuntimeException e) { - return CompletableResultCode.ofFailure(); - } - } - - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - - isShutdown = true; - - try { - return CompletableResultCode.ofSuccess(); - } catch (Exception e) { - return CompletableResultCode.ofFailure(); - } - } -} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java deleted file mode 100644 index 3aa95a7b..00000000 --- a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.trace.manager; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Scope; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.trace.IdGenerator; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import java.util.Objects; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TraceManager { - - private static final String INSTRUMENTATION_SCOPE_NAME = "instrument-transaction-scope"; - - private static volatile SdkTracerProvider provider; - - private static volatile OpenTelemetrySdk openTelemetrySdk; - - public static Tracer configure(final SpanExporter exporter) { - - provider = SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(exporter)) - .setIdGenerator(IdGenerator.random()) - .setSampler(Sampler.alwaysOn()) - .build(); - - openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(provider) - .build(); - - return openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME); - } - - public static void close() { - if (Objects.nonNull(provider) && Objects.nonNull(openTelemetrySdk)) { - provider.close(); - openTelemetrySdk.close(); - } - } - - public record SpanScope(Span span, Scope scope) { } -} diff --git a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java b/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java deleted file mode 100644 index 3dbbf577..00000000 --- a/java-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.trace.queue; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import org.traffichunter.javaagent.trace.dto.TraceInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum TraceQueue { - - INSTANCE, - ; - - private final BlockingQueue syncQ = new LinkedBlockingQueue<>(100); - - /** - * this method is non-blocking - * @return success : true, fail : false - */ - public boolean add(final TraceInfo trInfo) { - return syncQ.offer(trInfo); - } - - /** - * this method is blocking - */ - public TraceInfo poll() throws InterruptedException { - return syncQ.take(); - } - - public void removeAll() { - syncQ.clear(); - } - - public int size() { - return syncQ.size(); - } -} diff --git a/java-agent/java-agent-websocket/build.gradle b/java-agent/java-agent-websocket/build.gradle deleted file mode 100644 index 75d76e43..00000000 --- a/java-agent/java-agent-websocket/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.websocket' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-agent:java-agent-commons') - - implementation 'org.java-websocket:Java-WebSocket:1.5.7' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar b/java-agent/java-agent-websocket/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/java-agent-websocket/gradlew.bat b/java-agent/java-agent-websocket/gradlew.bat deleted file mode 100644 index 107acd32..00000000 --- a/java-agent/java-agent-websocket/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java deleted file mode 100644 index 5178d488..00000000 --- a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/MetricWebSocketClient.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.websocket; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.net.URI; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; -import org.traffichunter.javaagent.websocket.metadata.Metadata; - -/** - * The {@code MetricWebSocketClient} class extends {@link WebSocketClient} - * to provide specialized functionality for sending serialized metrics over a WebSocket connection. - * - *

Features:

- *
    - *
  • Automatically sends metadata when the connection is opened.
  • - *
  • Supports sending metrics as JSON or compressed binary data.
  • - *
  • Provides utility methods to check and manage WebSocket connection state.
  • - *
- * - * @see WebSocketClient - * @see SerializationByteArrayConverter - * @see Metadata - * @see MetricType - * - * @author yungwang-o - * @version 1.0.0 - */ -public class MetricWebSocketClient extends WebSocketClient { - - private static final Logger log = LoggerFactory.getLogger(MetricWebSocketClient.class); - - private final SerializationByteArrayConverter converter; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - private final Metadata metadata; - - public MetricWebSocketClient(final URI serverUri, final Metadata metadata) { - super(serverUri); - this.metadata = metadata; - this.objectMapper - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .registerModule(new JavaTimeModule()); - this.converter = new SerializationByteArrayConverter(objectMapper); - } - - @Override - public void onOpen(final ServerHandshake serverHandshake) { - log.info("websocket client opened"); - this.toSend(metadata); - } - - @Override - public void onMessage(final String s) { - log.info("websocket client received = {}", s); - } - - - @Override - public void onClose(final int i, final String s, final boolean b) { - log.info("websocket client closed"); - } - - @Override - public void onError(final Exception e) { - log.error("websocket client error = {}", e.getMessage()); - } - - public boolean isConnected() { - if(isOpen()) { - return true; - } - - try { - return super.connectBlocking(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return false; - } - - public boolean isConnected(final long timeOut, final TimeUnit timeUnit) { - if(isOpen()) { - return true; - } - - try { - return super.connectBlocking(timeOut, timeUnit); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return false; - } - - public void toSend(final M metric) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - try { - String s = objectMapper.writeValueAsString(metric); - this.send(s); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public void compressToSend(final M metric, final MetricType metricType) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - byte[] transform = converter.transform(metric, metricType); - - this.send(transform); - } - - public void toSend(final List metrics) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - try { - this.send(objectMapper.writeValueAsString(metrics)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java deleted file mode 100644 index 33f2304a..00000000 --- a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverter.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.websocket.converter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -/** - * The {@code SerializationByteArrayConverter} class provides utility methods for - * serializing objects into compressed byte arrays and deserializing compressed byte arrays - * back into objects. This is useful for efficient data transmission or storage. - * - *

Key Features:

- *
    - *
  • Serializes objects to compressed byte arrays with a metric type identifier.
  • - *
  • Deserializes compressed byte arrays back into objects of specified types.
  • - *
  • Supports gzip compression for optimized size reduction.
  • - *
- * - *

Usage:

- *
    - *
  • {@code transform(Object, MetricType)}: Converts an object to a compressed byte array.
  • - *
  • {@code inverseTransform(byte[], TypeReference)}: Converts a compressed byte array back into an object.
  • - *
- * - * @see ObjectMapper - * @see GZIPOutputStream - * @see GZIPInputStream - * @author yungwang-o - * @version 1.0.0 - */ -public class SerializationByteArrayConverter { - - private final ObjectMapper objectMapper; - - public SerializationByteArrayConverter(final ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - public byte[] transform(final Object obj, final MetricType metricType) { - byte[] data = serialize(obj); - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ - - try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) { - gzipOutputStream.write(data); - } - - byte[] compressByteArray = baos.toByteArray(); - byte[] result = new byte[baos.toByteArray().length + 1]; - - result[0] = metricType.value; - - System.arraycopy(compressByteArray, 0, result, 1, compressByteArray.length); - - return result; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private byte[] serialize(final Object object) { - try { - return objectMapper.writeValueAsBytes(object); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public enum MetricType { - SYSTEM_METRIC((byte) 1), - TRANSACTION_METRIC((byte) 2), - ; - - private final byte value; - - MetricType(final byte value) { - this.value = value; - } - } -} diff --git a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java b/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java deleted file mode 100644 index 78c25eed..00000000 --- a/java-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/metadata/Metadata.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.traffichunter.javaagent.websocket.metadata; - -import java.time.Instant; -import org.traffichunter.javaagent.commons.status.AgentStatus; - -public record Metadata( - String agentId, - String agentVersion, - String agentName, - Instant startTime, - AgentStatus status) { - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private String agentId; - private String agentVersion; - private String agentName; - private Instant startTime; - private AgentStatus status; - - public Builder agentId(String agentId) { - this.agentId = agentId; - return this; - } - - public Builder agentVersion(String agentVersion) { - this.agentVersion = agentVersion; - return this; - } - - public Builder agentName(String agentName) { - this.agentName = agentName; - return this; - } - - public Builder startTime(Instant startTime) { - this.startTime = startTime; - return this; - } - - public Builder status(AgentStatus status) { - this.status = status; - return this; - } - - public Metadata build() { - return new Metadata(agentId, agentVersion, agentName, startTime, status); - } - } -} diff --git a/java-agent/plugin/build.gradle b/java-agent/plugin/build.gradle deleted file mode 100644 index 418f1ce6..00000000 --- a/java-agent/plugin/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.plugin' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -subprojects { - apply plugin: 'java' - - dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation 'net.bytebuddy:byte-buddy:1.15.5' - - implementation 'io.opentelemetry:opentelemetry-api:1.45.0' - implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' - } -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/plugin/gradle/wrapper/gradle-wrapper.jar b/java-agent/plugin/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties b/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 1af9e093..00000000 --- a/java-agent/plugin/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/java-agent/plugin/gradlew b/java-agent/plugin/gradlew deleted file mode 100755 index 1aa94a42..00000000 --- a/java-agent/plugin/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java-agent/plugin/gradlew.bat b/java-agent/plugin/gradlew.bat deleted file mode 100644 index 93e3f59f..00000000 --- a/java-agent/plugin/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-agent/plugin/plugin-sdk/build.gradle b/java-agent/plugin/plugin-sdk/build.gradle deleted file mode 100644 index a121ab7a..00000000 --- a/java-agent/plugin/plugin-sdk/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.plugin.plugin-sdk' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java deleted file mode 100644 index e88902ff..00000000 --- a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.constant; - -public enum PluginConstant { - - SPRING_BOOT, - MYSQL, - KAFKA, - ELASTICSEARCH, - POSTGRES -} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java deleted file mode 100644 index e9ef6630..00000000 --- a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.instrumentation; - -import net.bytebuddy.agent.builder.AgentBuilder; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.type.TypeInstrumentation; - -public interface PluginInstrumentation extends TypeInstrumentation { - - void transform(AgentBuilder.Transformer transformer, ClassLoader classLoader); -} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java deleted file mode 100644 index 730431dc..00000000 --- a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.instrumentation.type; - -import static net.bytebuddy.matcher.ElementMatchers.any; - -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; - -public interface TypeInstrumentation { - - default ElementMatcher.Junction classLoaderMatcher() { return any(); } - - ElementMatcher typeMatcher(); - - Junction ignorePackage(); -} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java deleted file mode 100644 index 1ff60c68..00000000 --- a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.loader; - -import java.util.List; - -public interface PluginLoader

{ - - List

loadModules(ClassLoader classLoader); -} diff --git a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java b/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java deleted file mode 100644 index fa750825..00000000 --- a/java-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.loader; - -import java.util.ArrayList; -import java.util.List; -import java.util.ServiceLoader; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; - -public class TrafficHunterPluginLoader implements PluginLoader { - - @Override - public List loadModules(final ClassLoader classLoader) { - - List loadPlugIn = new ArrayList<>(); - - ServiceLoader loader = ServiceLoader.load(PluginInstrumentation.class, classLoader); - - for(PluginInstrumentation plugIn : loader) { - loadPlugIn.add(plugIn); - } - - return loadPlugIn; - } -} diff --git a/java-agent/plugin/spring-webmvc/build.gradle b/java-agent/plugin/spring-webmvc/build.gradle deleted file mode 100644 index 6ecb28b5..00000000 --- a/java-agent/plugin/spring-webmvc/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.plugin.spring-webmvc' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - implementation project(':java-agent:plugin:plugin-sdk') -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java b/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java deleted file mode 100644 index 37c0c7bd..00000000 --- a/java-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.traffichunter.javaagent.plugin.spring.webmvc; - -import static net.bytebuddy.matcher.ElementMatchers.named; - -import net.bytebuddy.agent.builder.AgentBuilder.Transformer; -import net.bytebuddy.asm.Advice.OnMethodEnter; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; - -public class SpringWebMvcPluginInstrumentation implements PluginInstrumentation { - - @Override - public void transform(final Transformer transformer, final ClassLoader classLoader) { - - } - - @Override - public ElementMatcher typeMatcher() { - return named("org.springframework.web.servlet.DispatcherServlet"); - } - - @Override - public Junction ignorePackage() { - return null; - } - - public static class SpringWebMvcDispatcherServletAdvice { - - @OnMethodEnter - public static void enter() { - - } - } -} diff --git a/java-agent/src/jmh/java/ygo/benchmark/Benchmark.java b/java-agent/src/jmh/java/ygo/benchmark/Benchmark.java deleted file mode 100644 index b2546d82..00000000 --- a/java-agent/src/jmh/java/ygo/benchmark/Benchmark.java +++ /dev/null @@ -1,37 +0,0 @@ -package ygo.benchmark; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPOutputStream; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; - -@State(Scope.Thread) -@BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -public class Benchmark { - - private final ObjectMapper objectMapper; - - public Benchmark() { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JavaTimeModule()); - } - - private byte[] compress(byte[] bytes) { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ - try (GZIPOutputStream gzip = new GZIPOutputStream(baos)){ - gzip.write(bytes); - } - return baos.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/AgentStatus.java b/java-agent/src/main/java/ygo/traffichunter/agent/AgentStatus.java deleted file mode 100644 index 8eaabc12..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/AgentStatus.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum AgentStatus { - INITIALIZED, - RUNNING, - EXIT -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/TrafficHunterAgent.java b/java-agent/src/main/java/ygo/traffichunter/agent/TrafficHunterAgent.java deleted file mode 100644 index a9bce364..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/TrafficHunterAgent.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent; - -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; -import ygo.traffichunter.agent.child.FaultTolerantTrafficHunterAgent; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -/** - *

- * The {@code TrafficHunterAgent} class is responsible for holding and managing configuration - * details required to set up and execute traffic monitoring or management tasks. - * This class provides a fluent API for constructing and customizing the configuration - * properties, such as scheduling intervals, target URIs, and resource locations. - *

- * - *

Example Usage:

- *
{@code
- * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
- *     .name("MyAgent")
- *     .targetUri("localhost:8888")
- *     .locationJar("/path/to/agent.jar")
- *     .scheduleInterval(10)
- *     .scheduleTimeUnit(TimeUnit.MINUTES)
- *     .complete();
- * }
- * - *

Extensibility:

- *
    - *
  • The class can be extended with custom behavior, as seen in {@link FaultTolerantTrafficHunterAgent}.
  • - *
- * - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgent { - - private static final Logger log = Logger.getLogger(TrafficHunterAgent.class.getName()); - - protected int scheduleInterval; - - protected String targetUri; - - protected String jar; - - protected final String uri; - - protected TimeUnit timeUnit; - - protected String name; - - protected TrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { - this.name = trafficHunterAgent.name; - this.scheduleInterval = trafficHunterAgent.scheduleInterval; - this.targetUri = trafficHunterAgent.targetUri; - this.uri = trafficHunterAgent.uri; - this.timeUnit = trafficHunterAgent.timeUnit; - this.jar = trafficHunterAgent.jar; - } - - protected TrafficHunterAgent(final String uri) { - this.uri = uri; - } - - public static TrafficHunterAgent connect(final String serverUrl) { - return new TrafficHunterAgent(serverUrl); - } - - public FaultTolerantTrafficHunterAgent faultTolerant() { - return new FaultTolerantTrafficHunterAgent(this); - } - - public TrafficHunterAgent name(final String name) { - this.name = name; - return this; - } - - public TrafficHunterAgent targetUri(final String targetUri) { - this.targetUri = targetUri; - return this; - } - - public TrafficHunterAgent locationJar(final String jar) { - this.jar = jar; - return this; - } - - public TrafficHunterAgent scheduleInterval(final int scheduleInterval) { - this.scheduleInterval = scheduleInterval; - return this; - } - - public TrafficHunterAgent scheduleTimeUnit(final TimeUnit timeUnit) { - this.timeUnit = timeUnit; - return this; - } - - public TrafficHunterAgentProperty complete() { - return new TrafficHunterAgentProperty(name, targetUri, jar, scheduleInterval, uri, timeUnit); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/banner/AsciiBanner.java b/java-agent/src/main/java/ygo/traffichunter/agent/banner/AsciiBanner.java deleted file mode 100644 index 811030f5..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/banner/AsciiBanner.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.banner; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Objects; -import java.util.stream.Collectors; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; - -/** - * banner print. - * @author yungwang-o - * @version 1.0.0 - */ -public class AsciiBanner { - - private static final String BANNER_NAME = "agent-banner.txt"; - - public void print(final AgentMetadata metadata) { - try (final InputStream in = AsciiBanner.class.getClassLoader().getResourceAsStream(BANNER_NAME)) { - - BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(in))); - - String banner = reader.lines() - .map(line -> line - .replace("${version}", metadata.agentVersion()) - .replace("${java.version}", System.getProperty("java.version")) - .replace("${java.specification}", System.getProperty("java.specification.version")) - .replace("${jdk}", System.getProperty("java.vendor")) - .replace("${time}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))) - .collect(Collectors.joining(System.lineSeparator())); - - System.out.println(banner + "\n"); - - } catch (IOException ignored) { - - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgent.java b/java-agent/src/main/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgent.java deleted file mode 100644 index af054d38..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgent.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.child; - -import java.util.logging.Logger; -import ygo.traffichunter.agent.TrafficHunterAgent; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.retry.backoff.BackOffPolicy; - -/** - * The {@code FaultTolerantTrafficHunterAgent} class extends the functionality of - * {@link TrafficHunterAgent} by adding fault-tolerant capabilities, such as retry logic - * and back-off policies. This class is designed to handle scenarios where failures - * may occur and ensures that the agent can recover gracefully. - * - *

Example Usage:

- *
{@code
- * TrafficHunterProperty property = TrafficHunterAgent.connect("localhost:8080")
- *     .name("ResilientAgent")
- *     .targetUri("localhost:8888")
- *     .locationJar("/path/to/agent.jar")
- *     .scheduleInterval(10)
- *     .scheduleTimeUnit(TimeUnit.MINUTES)
- *     .faultTolerant()
- *     .retry(5) // Set maximum retry attempts to 5
- *     .backOffPolicy(new ExponentialBackOffPolicy()) // Use exponential back-off policy
- *     .complete();
- * }
- * - * @see TrafficHunterAgent - * @see BackOffPolicy - * @author yungwang-o - * @version 1.0.0 - */ -public class FaultTolerantTrafficHunterAgent extends TrafficHunterAgent { - - private static final Logger log = Logger.getLogger(FaultTolerantTrafficHunterAgent.class.getName()); - private BackOffPolicy backOffPolicy; - private int maxAttempt; - - public FaultTolerantTrafficHunterAgent(final TrafficHunterAgent trafficHunterAgent) { - super(trafficHunterAgent); - } - - public FaultTolerantTrafficHunterAgent retry(final int maxAttempt) { - this.maxAttempt = maxAttempt; - return this; - } - - public FaultTolerantTrafficHunterAgent backOffPolicy(final BackOffPolicy backOffPolicy) { - this.backOffPolicy = backOffPolicy; - return this; - } - - @Override - public TrafficHunterAgentProperty complete() { - return new TrafficHunterAgentProperty( - this.name, - this.targetUri, - this.jar, - this.scheduleInterval, - this.uri, - this.timeUnit, - maxAttempt, - backOffPolicy - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java deleted file mode 100644 index 6ab51553..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/AgentExecutionEngine.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine; - -import java.lang.instrument.Instrumentation; -import java.time.Duration; -import java.time.Instant; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.banner.AsciiBanner; -import ygo.traffichunter.agent.engine.context.AgentExecutableContext; -import ygo.traffichunter.agent.engine.context.configuration.ConfigurableContextInitializer; -import ygo.traffichunter.agent.engine.context.execute.TrafficHunterAgentExecutableContext; -import ygo.traffichunter.agent.engine.env.ConfigurableEnvironment; -import ygo.traffichunter.agent.engine.env.yaml.YamlConfigurableEnvironment; -import ygo.traffichunter.agent.engine.lifecycle.LifeCycle; -import ygo.traffichunter.agent.engine.queue.SyncQueue; -import ygo.traffichunter.agent.engine.sender.manager.MetricSendSessionManager; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.trace.opentelemetry.TraceManager; - -/** - *

- * The {@code AgentExecutionEngine} class is the core execution engine responsible for - * initializing, configuring, running, and shutting down the TrafficHunter Agent. - * This class encapsulates all the necessary steps to bootstrap the agent, manage its - * lifecycle, and ensure proper cleanup upon application termination. - *

- *

Purpose:

- *
    - *
  • Initialize and configure the TrafficHunter Agent with environment-specific settings.
  • - *
  • Run the agent in a dedicated thread to monitor and manage traffic.
  • - *
  • Register and manage shutdown hooks to ensure proper cleanup during termination.
  • - *
- * - *

Key Components:

- *
    - *
  • {@link TrafficHunterAgentShutdownHook} - Manages graceful shutdown of resources.
  • - *
  • {@link ConfigurableEnvironment} - Loads environment-specific configurations.
  • - *
  • {@link AgentRunner} - Executes the agent's core logic in a separate thread.
  • - *
- * - *

Limitations:

- *
    - *
  • This class assumes that the {@code Instrumentation} object is correctly provided at runtime.
  • - *
  • Agent initialization failures may result in partial cleanup if not handled carefully.
  • - *
- * - * @see TrafficHunterAgentShutdownHook - * @see AgentExecutionEngine.AgentRunner - * @see ConfigurableEnvironment - * @see Instrumentation - * @author yungwang-o - * @version 1.0.0 - */ -public final class AgentExecutionEngine { - - private static final Logger log = LoggerFactory.getLogger(AgentExecutionEngine.class); - - private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); - - private final AsciiBanner asciiBanner = new AsciiBanner(); - - private final ConfigurableEnvironment environment; - - private final Instrumentation inst; - - private AgentExecutionEngine(final String args, final Instrumentation inst) { - this.inst = inst; - this.environment = new YamlConfigurableEnvironment(args); - } - - /** - * Initializes and executes the TrafficHunter Agent. - *

This method performs the following tasks:

- *
    - *
  • Loads environment-specific configurations using {@link YamlConfigurableEnvironment}.
  • - *
  • Initializes the agent's execution context and metadata.
  • - *
  • Registers shutdown hooks for cleanup during termination.
  • - *
  • Starts the agent's core logic in a dedicated thread.
  • - *
- */ - private void run() { - StartUp startUp = new StartUp(); - Instant startTime = startUp.getStartTime(); - final AgentExecutableContext context = new TrafficHunterAgentExecutableContext(environment, shutdownHook); - if(!shutdownHook.isEnabledShutdownHook()) { - shutdownHook.enableShutdownHook(); - } - ConfigurableContextInitializer configurableContextInitializer = context.init(); - configurableContextInitializer.retransform(inst); - TrafficHunterAgentProperty property = configurableContextInitializer.property(); - AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( - startTime, - AgentStatus.INITIALIZED - ); - context.addAgentStateEventListener(metadata); - asciiBanner.print(metadata); - AgentRunner runner = new AgentRunner(property, context, metadata); - Thread runnerThread = new Thread(runner); - runnerThread.setName("TrafficHunterAgentRunnerThread"); - if(context.isInit()) { - log.info("Agent initialization completed."); - runnerThread.start(); - registryShutdownHook(context, runner); - context.close(); - } - - log.info("Started TrafficHunter Agent in {} second", String.format("%.3f", startUp.getUpTime())); - } - - /** - * Registers shutdown hooks for the agent's cleanup operations. - * - * @param context The execution context of the agent. - * @param runner The agent runner instance responsible for managing execution. - */ - private void registryShutdownHook(final AgentExecutableContext context, final AgentRunner runner) { - shutdownHook.addRuntimeShutdownHook(TraceManager::close); - shutdownHook.addRuntimeShutdownHook(SyncQueue.INSTANCE::removeAll); - shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); - shutdownHook.addRuntimeShutdownHook(runner::close); - } - - public static void run(final String args, final Instrumentation inst) { - new AgentExecutionEngine(System.getProperty(args), inst).run(); - } - - /** - * The {@code StartUp} class extends {@link LifeCycle} to measure the agent's - * startup durations. - * - *

Features:

- *
    - *
  • Tracks the agent's start time and end time.
  • - *
  • Calculates the total uptime.
  • - *
- */ - private static class StartUp extends LifeCycle { - - public StartUp() { - super(); - } - - @Override - public Instant getStartTime() { - return this.startTime; - } - - @Override - public Instant getEndTime() { - if(endTime == null) { - this.endTime = Instant.now(); - } - return endTime; - } - - @Override - public Double getUpTime() { - if(getStartTime() == null && getEndTime() == null) { - throw new IllegalStateException("No start time or end time specified"); - } - - return Duration.between(getStartTime(), getEndTime()).toMillis() / 1_000.0; - } - } - - /** - * The {@code AgentRunner} class implements {@link Runnable} to execute the core logic - * of the TrafficHunter Agent in a separate thread. It initializes the session manager - * and orchestrates agent operations after the target application is loaded. - * - *

Features:

- *
    - *
  • Introduces a delay to ensure the target application is fully loaded before execution.
  • - *
  • Manages the lifecycle of the {@link MetricSendSessionManager}.
  • - *
- */ - private static final class AgentRunner implements Runnable { - - private final MetricSendSessionManager sessionManager; - - public AgentRunner(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.sessionManager = new MetricSendSessionManager(property, context, metadata); - } - - /** - * Executes the agent's core logic. Introduces a delay to ensure that - * the target application is fully loaded before starting. - */ - @Override - public void run() { - try { - log.info("Waiting for Agent Runner..."); - Thread.sleep(8000); - sessionManager.run(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - public void close() { - sessionManager.close(); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/TrafficHunterAgentShutdownHook.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/TrafficHunterAgentShutdownHook.java deleted file mode 100644 index 5a951816..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/TrafficHunterAgentShutdownHook.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine; - -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@code TrafficHunterAgentShutdownHook} class is responsible for managing and - * registering shutdown hooks to execute specific actions when the Java application - * terminates. This class allows users to add custom {@link Runnable} actions that - * will be executed gracefully during the shutdown process. - * - * @see Runtime#addShutdownHook(Thread) - * @see Runnable - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgentShutdownHook implements Runnable { - - private static final Logger log = LoggerFactory.getLogger(TrafficHunterAgentShutdownHook.class); - - private volatile boolean enabledShutdownHook = false; - - private final Set actions = ConcurrentHashMap.newKeySet(); - - public void enableShutdownHook() { - enabledShutdownHook = true; - } - - public void addRuntimeShutdownHook(final Runnable action) { - actions.add(action); - } - - @Override - public void run() { - log.info("register shutdown hook"); - - if(!enabledShutdownHook) { - log.info("shutdown hook not enabled"); - return; - } - - for(Runnable action : actions) { - Runtime.getRuntime().addShutdownHook(new Thread(action, nameShutdownThread(action.getClass().getName()))); - } - } - - public boolean isEnabledShutdownHook() { - return enabledShutdownHook; - } - - private static String nameShutdownThread(final String threadName) { - return threadName + "-ShutdownHook"; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/AbstractMBeanMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/AbstractMBeanMetricCollector.java deleted file mode 100644 index e085b79c..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/AbstractMBeanMetricCollector.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect; - -import java.lang.management.ManagementFactory; -import java.util.Set; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -/** - * The {@code AbstractMBeanMetricCollector} abstract class provides a base implementation - * for collecting metrics using the JMX {@link MBeanServer}. - * - *

Features:

- *
    - *
  • Extends {@link MetricCollector} for metric collection.
  • - *
  • Provides helper methods to query MBeans and retrieve attributes.
  • - *
  • Handles exceptions gracefully through the nested {@link MetricCollectionException} class.
  • - *
- * - * @see MetricCollector - * @see MBeanServer - * - * @author yungwang-o - * @version 1.0.0 - */ -public abstract class AbstractMBeanMetricCollector implements MetricCollector { - - protected final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); - - protected Set findObjectName(final String objectName) { - try { - return mBeanServer.queryNames(new ObjectName(objectName), null); - } catch (MalformedObjectNameException e) { - throw new RuntimeException(e); - } - } - - protected
A getAttribute(final ObjectName name, final String attribute, final Class type) { - try { - - return type.cast(mBeanServer.getAttribute(name, attribute)); - } catch (Exception e) { - throw new MetricCollectionException("Failed to get attribute: " + attribute, e); - } - } - - public static class MetricCollectionException extends RuntimeException { - - public MetricCollectionException() { - super(); - } - - public MetricCollectionException(final String message) { - super(message); - } - - public MetricCollectionException(final String message, final Throwable cause) { - super(message, cause); - } - - public MetricCollectionException(final Throwable cause) { - super(cause); - } - - protected MetricCollectionException(final String message, final Throwable cause, - final boolean enableSuppression, - final boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollectSupport.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollectSupport.java deleted file mode 100644 index 946a1780..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollectSupport.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect; - -import java.io.IOException; -import java.time.Instant; -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.collect.dbcp.hikari.HikariCPMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.cpu.CpuMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.gc.GarbageCollectionMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.memory.MemoryMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.runtime.RuntimeMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.thread.ThreadMetricCollector; -import ygo.traffichunter.agent.engine.collect.web.tomcat.TomcatMetricCollector; -import ygo.traffichunter.agent.engine.jvm.JVMSelector; -import ygo.traffichunter.agent.engine.metric.systeminfo.SystemInfo; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -/** - * The {@code MetricCollectSupport} class provides functionality for collecting system metrics - * from the local JVM or a target JVM via JMX. - * - *

Features:

- *
    - *
  • Integrates multiple metric collectors for memory, CPU, threads, garbage collection, - * runtime, Tomcat, and HikariCP metrics.
  • - *
  • Supports collecting metrics from the local JVM or a remote JVM identified by a JMX path.
  • - *
  • Combines collected metrics into a unified {@link SystemInfo} object.
  • - *
- * - * @see SystemInfo - * @see MemoryMetricCollector - * @see CpuMetricCollector - * @see ThreadMetricCollector - * @see GarbageCollectionMetricCollector - * @see RuntimeMetricCollector - * @see TomcatMetricCollector - * @see HikariCPMetricCollector - * - * @author yungwang-o - * @version 1.0.0 - */ - -public class MetricCollectSupport { - - private static final Logger log = LoggerFactory.getLogger(MetricCollectSupport.class); - - private final MemoryMetricCollector collectorMemory; - - private final CpuMetricCollector collectorCpu; - - private final ThreadMetricCollector collectorThread; - - private final GarbageCollectionMetricCollector collectorGC; - - private final RuntimeMetricCollector collectorRuntime; - - private final TomcatMetricCollector collectorTomcat; - - private final HikariCPMetricCollector collectorHikari; - - public MetricCollectSupport(final TrafficHunterAgentProperty property) { - this.collectorMemory = new MemoryMetricCollector(); - this.collectorCpu = new CpuMetricCollector(); - this.collectorThread = new ThreadMetricCollector(); - this.collectorGC = new GarbageCollectionMetricCollector(); - this.collectorRuntime = new RuntimeMetricCollector(); - this.collectorTomcat = new TomcatMetricCollector(property); - this.collectorHikari = new HikariCPMetricCollector(); - } - - public SystemInfo collect(final String targetJVMPath) { - - try (final JMXConnector jmxConnector = JMXConnectorFactory.connect(JVMSelector.getVMXServiceUrl(targetJVMPath))) { - - final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection(); - - return new SystemInfo( - Instant.now(), - collectorMemory.collect(mbsc), - collectorThread.collect(mbsc), - collectorCpu.collect(mbsc), - collectorGC.collect(mbsc), - collectorRuntime.collect(mbsc), - collectorTomcat.collect(mbsc), - collectorHikari.collect(mbsc) - ); - - } catch (IOException e) { - log.error("Failed to start local management agent = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - public SystemInfo collect() { - return new SystemInfo( - Instant.now(), - collectorMemory.collect(), - collectorThread.collect(), - collectorCpu.collect(), - collectorGC.collect(), - collectorRuntime.collect(), - collectorTomcat.collect(), - collectorHikari.collect() - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollector.java deleted file mode 100644 index 0e41a340..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/MetricCollector.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect; - -import java.util.List; -import javax.management.MBeanServerConnection; - -/** - * The {@code MetricCollector} interface defines a contract for collecting metrics. - * Implementations can collect metrics from various sources, such as the local JVM - * or an MBeanServer. - * - *

Features:

- *
    - *
  • Provides methods for collecting a single metric or all available metrics.
  • - *
  • Includes default implementations for optional operations.
  • - *
- * - * @param The type of metric being collected. - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface MetricCollector { - - default T collect(MBeanServerConnection mbsc) { - return null; - } - - T collect(); - - default List collectAll() {return null;} -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/dbcp/hikari/HikariCPMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/dbcp/hikari/HikariCPMetricCollector.java deleted file mode 100644 index bb9efb92..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/dbcp/hikari/HikariCPMetricCollector.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.dbcp.hikari; - -import javax.management.ObjectName; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.dbcp.HikariDbcpInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class HikariCPMetricCollector extends AbstractMBeanMetricCollector { - - @Override - public HikariDbcpInfo collect() { - try { - - //The default name for a Hikari connection pool is "HikariPool-1" - ObjectName hikariInfo = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)"); - - int activeConnections = getAttribute(hikariInfo, "ActiveConnections", Integer.class); - int idleConnections = getAttribute(hikariInfo, "IdleConnections", Integer.class); - int totalConnections = getAttribute(hikariInfo, "TotalConnections", Integer.class); - int threadsAwaitingConnections = getAttribute(hikariInfo, "ThreadsAwaitingConnection", Integer.class); - - return new HikariDbcpInfo( - activeConnections, - idleConnections, - totalConnections, - threadsAwaitingConnections - ); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/cpu/CpuMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/cpu/CpuMetricCollector.java deleted file mode 100644 index 5403c323..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/cpu/CpuMetricCollector.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.systeminfo.cpu; - -import com.sun.management.OperatingSystemMXBean; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.systeminfo.cpu.CpuStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class CpuMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(CpuStatusInfo.class.getName()); - - @Override - public CpuStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final OperatingSystemMXBean osMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, - OperatingSystemMXBean.class - ); - - return new CpuStatusInfo( - osMXBean.getCpuLoad(), - osMXBean.getProcessCpuLoad(), - osMXBean.getAvailableProcessors() - ); - } catch (IOException e) { - log.warning("Failed to get cpu metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public CpuStatusInfo collect() { - final OperatingSystemMXBean osMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - - return new CpuStatusInfo( - osMXBean.getCpuLoad(), - osMXBean.getProcessCpuLoad(), - osMXBean.getAvailableProcessors() - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java deleted file mode 100644 index c455864a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/gc/GarbageCollectionMetricCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.systeminfo.gc; - -import java.io.IOException; -import java.lang.management.GarbageCollectorMXBean; -import java.lang.management.ManagementFactory; -import java.util.List; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.collect.systeminfo.memory.MemoryMetricCollector; -import ygo.traffichunter.agent.engine.metric.systeminfo.gc.GarbageCollectionStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.gc.collections.GarbageCollectionTime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class GarbageCollectionMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); - - @Override - public GarbageCollectionStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final List mxBeans = ManagementFactory.getPlatformMXBeans(mbsc, - GarbageCollectorMXBean.class); - - final List garbageCollectionTimes = mxBeans - .stream() - .map(GarbageCollectionTime::new) - .toList(); - - return new GarbageCollectionStatusInfo(garbageCollectionTimes); - } catch (IOException e) { - log.warning("Failed to get GC metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public GarbageCollectionStatusInfo collect() { - final List mxBeans = ManagementFactory.getGarbageCollectorMXBeans(); - - final List garbageCollectionTimes = mxBeans - .stream() - .map(GarbageCollectionTime::new) - .toList(); - - return new GarbageCollectionStatusInfo(garbageCollectionTimes); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/memory/MemoryMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/memory/MemoryMetricCollector.java deleted file mode 100644 index b912aa3d..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/memory/MemoryMetricCollector.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.systeminfo.memory; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.systeminfo.memory.MemoryStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.memory.MemoryStatusInfo.MemoryUsage; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class MemoryMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(MemoryMetricCollector.class.getName()); - - @Override - public MemoryStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.MEMORY_MXBEAN_NAME, - MemoryMXBean.class - ); - - final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - - return new MemoryStatusInfo( - new MemoryUsage( - heapMemoryUsage.getInit(), - heapMemoryUsage.getUsed(), - heapMemoryUsage.getCommitted(), - heapMemoryUsage.getMax() - ), - new MemoryUsage( - nonHeapMemoryUsage.getInit(), - nonHeapMemoryUsage.getUsed(), - nonHeapMemoryUsage.getCommitted(), - nonHeapMemoryUsage.getMax() - ) - ); - } catch (IOException e) { - log.warning("Failed to get heap memory metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public MemoryStatusInfo collect() { - final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); - - final java.lang.management.MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - final java.lang.management.MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - - return new MemoryStatusInfo( - new MemoryUsage( - heapMemoryUsage.getInit(), - heapMemoryUsage.getUsed(), - heapMemoryUsage.getCommitted(), - heapMemoryUsage.getMax() - ), - new MemoryUsage( - nonHeapMemoryUsage.getInit(), - nonHeapMemoryUsage.getUsed(), - nonHeapMemoryUsage.getCommitted(), - nonHeapMemoryUsage.getMax() - ) - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java deleted file mode 100644 index 2b5b5970..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/runtime/RuntimeMetricCollector.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.systeminfo.runtime; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.systeminfo.runtime.RuntimeStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RuntimeMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(RuntimeStatusInfo.class.getName()); - - @Override - public RuntimeStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.RUNTIME_MXBEAN_NAME, - RuntimeMXBean.class - ); - - return new RuntimeStatusInfo( - runtimeMXBean.getStartTime(), - runtimeMXBean.getUptime(), - runtimeMXBean.getVmName(), - runtimeMXBean.getVmVersion() - ); - } catch (IOException e) { - log.warning("Failed to get runtime metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public RuntimeStatusInfo collect() { - final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); - - return new RuntimeStatusInfo( - runtimeMXBean.getStartTime(), - runtimeMXBean.getUptime(), - runtimeMXBean.getVmName(), - runtimeMXBean.getVmVersion() - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/thread/ThreadMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/thread/ThreadMetricCollector.java deleted file mode 100644 index ca5a42c6..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/systeminfo/thread/ThreadMetricCollector.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.systeminfo.thread; - -import com.sun.management.ThreadMXBean; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.util.logging.Logger; -import javax.management.MBeanServerConnection; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.systeminfo.thread.ThreadStatusInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class ThreadMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = Logger.getLogger(ThreadMetricCollector.class.getName()); - - @Override - public ThreadStatusInfo collect(final MBeanServerConnection mbsc) { - try { - final ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( - mbsc, - ManagementFactory.THREAD_MXBEAN_NAME, - ThreadMXBean.class - ); - - return new ThreadStatusInfo( - threadMXBean.getThreadCount(), - threadMXBean.getPeakThreadCount(), - threadMXBean.getTotalStartedThreadCount() - ); - } catch (IOException e) { - log.warning("Failed to get thread metric: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public ThreadStatusInfo collect() { - final ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); - - return new ThreadStatusInfo( - threadMXBean.getThreadCount(), - threadMXBean.getPeakThreadCount(), - threadMXBean.getTotalStartedThreadCount() - ); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/web/tomcat/TomcatMetricCollector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/web/tomcat/TomcatMetricCollector.java deleted file mode 100644 index 83939ae5..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/collect/web/tomcat/TomcatMetricCollector.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.collect.web.tomcat; - -import javax.management.ObjectName; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.collect.AbstractMBeanMetricCollector; -import ygo.traffichunter.agent.engine.metric.web.tomcat.TomcatWebServerInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.request.TomcatRequestInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.thread.TomcatThreadPoolInfo; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TomcatMetricCollector extends AbstractMBeanMetricCollector { - - private static final Logger log = LoggerFactory.getLogger(TomcatMetricCollector.class.getName()); - - private final TrafficHunterAgentProperty property; - - public TomcatMetricCollector(final TrafficHunterAgentProperty property) { - this.property = property; - } - - @Override - public TomcatWebServerInfo collect() { - - try { - ObjectName threadPoolMbean = new ObjectName("Tomcat:type=ThreadPool,name=" + getPort()); - ObjectName requestMBean = new ObjectName("Tomcat:type=GlobalRequestProcessor,name=" + getPort()); - - int maxThreads = getAttribute(threadPoolMbean, "maxThreads", Integer.class); - int currentThreads = getAttribute(threadPoolMbean, "currentThreadCount", Integer.class); - int currentThreadsBusy = getAttribute(threadPoolMbean, "currentThreadsBusy", Integer.class); - - int requestCount = getAttribute(requestMBean, "requestCount", Integer.class); - long bytesReceived = getAttribute(requestMBean, "bytesReceived", Long.class); - long bytesSent = getAttribute(requestMBean, "bytesSent", Long.class); - long processingTime = getAttribute(requestMBean, "processingTime", Long.class); - int errorCount = getAttribute(requestMBean, "errorCount", Integer.class); - - return new TomcatWebServerInfo( - new TomcatThreadPoolInfo(maxThreads, currentThreads, currentThreadsBusy), - new TomcatRequestInfo(requestCount, bytesReceived, bytesSent, processingTime, errorCount) - ); - } catch (Exception e) { - log.error("Failed to collect metrics = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - /** - * target uri -> localhost:8080 - *
- * translation -> localhost:8080 -> http-nio-8080 - * @return port - */ - private String getPort() { - final String port = property.targetUri().split(":")[1]; - return String.format("\"http-nio-%s\"", port); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/AgentExecutableContext.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/AgentExecutableContext.java deleted file mode 100644 index d9fdcd33..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/AgentExecutableContext.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.context; - -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.context.configuration.ConfigurableContextInitializer; -import ygo.traffichunter.agent.engine.env.ConfigurableEnvironment; -import ygo.traffichunter.agent.event.context.AgentContextStateEventHandler; - -/** - * The {@code AgentExecutableContext} interface defines the contract for managing - * the lifecycle of an agent's execution context. It includes methods for initializing - * the context, managing its state, and handling lifecycle events. - * - *

Features:

- *
    - *
  • Extends {@link AgentContextStateEventHandler} for state event listener management.
  • - *
  • Provides methods to initialize, close, and query the context's status.
  • - *
  • Supports dynamic updates to the agent's execution status.
  • - *
- * - * @see AgentContextStateEventHandler - * @see ConfigurableContextInitializer - * @see ConfigurableEnvironment - * @see AgentStatus - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentExecutableContext extends AgentContextStateEventHandler { - - ConfigurableContextInitializer init(); - - void close(); - - ConfigurableEnvironment getEnvironment(); - - AgentStatus getStatus(); - - boolean isInit(); - - boolean isRunning(); - - boolean isStopped(); - - void setStatus(AgentStatus status); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java deleted file mode 100644 index 636a4f5b..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializer.java +++ /dev/null @@ -1,189 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.context.configuration; - -import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.named; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import java.io.InputStream; -import java.lang.instrument.Instrumentation; -import java.time.Instant; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.builder.AgentBuilder.Default; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.asm.Advice.Enter; -import net.bytebuddy.asm.Advice.OnMethodEnter; -import net.bytebuddy.asm.Advice.OnMethodExit; -import net.bytebuddy.asm.Advice.Origin; -import net.bytebuddy.asm.Advice.Thrown; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; -import net.bytebuddy.matcher.ElementMatchers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.env.ConfigurableEnvironment; -import ygo.traffichunter.agent.engine.env.Environment; -import ygo.traffichunter.agent.engine.instrument.annotation.AnnotationPath; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.trace.opentelemetry.TraceExporter; -import ygo.traffichunter.trace.opentelemetry.TraceManager; -import ygo.traffichunter.trace.opentelemetry.TraceManager.SpanScope; -import ygo.traffichunter.util.UUIDGenerator; - -/** - * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, - * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. - * - *

Features:

- *
    - *
  • Loads properties using a configurable environment.
  • - *
  • Sets up agent metadata based on the current environment and runtime status.
  • - *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • - *
- * - * @see ConfigurableEnvironment - * @see TrafficHunterAgentProperty - * @see ByteBuddy - * @see AgentMetadata - * - * @author yungwang-o - * @version 1.0.0 - */ -public class ConfigurableContextInitializer { - - private static final Logger log = LoggerFactory.getLogger(ConfigurableContextInitializer.class); - - private final ConfigurableEnvironment env; - - public ConfigurableContextInitializer(final ConfigurableEnvironment env) { - this.env = env; - } - - public TrafficHunterAgentProperty property() { - return env.load(); - } - - public TrafficHunterAgentProperty property(final InputStream is) { - return env.load(is); - } - - public void retransform(final Instrumentation inst) { - new Default() - .ignore(ignoreMatchPackage()) - .type(getSpringComponentMatcher()) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(TransactionAdvise.class).on(isMethod())) - ).installOn(inst); - } - - public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { - - final String agentName = property().name(); - - return new AgentMetadata( - UUIDGenerator.generate(agentName), - Environment.VERSION.version(), - agentName, - startTime, - new AtomicReference<>(status) - ); - } - - private Junction ignoreMatchPackage() { - return ElementMatchers.nameStartsWith("java.") - .or(ElementMatchers.nameStartsWith("sun.")) - .or(ElementMatchers.nameStartsWith("jdk.")); - } - - private ElementMatcher getSpringComponentMatcher() { - return isAnnotatedWith(named(AnnotationPath.SERVICE.getPath())) - .or(isAnnotatedWith(named(AnnotationPath.REST_CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.REPOSITORY.getPath()))); - } - - /** - *

- * Intercepts method execution to create a span for tracing. - * Handles span lifecycle, recording exceptions, and closing the scope. - *

- * - *

Note: Ensure the class has a public access modifier to avoid - * visibility issues when used with external components or frameworks like ByteBuddy. - * Using a private or package-private access modifier may result in runtime errors - * due to restricted access.

- * - * @see TraceManager - */ - public static class TransactionAdvise { - - public static final Tracer tracer = TraceManager.configure(new TraceExporter()); - - @OnMethodEnter - public static SpanScope enter(@Origin final String method) { - - Span currentSpan = Span.current(); - - Span span = tracer.spanBuilder(method) - .setParent(Context.current().with(currentSpan)) - .setAttribute("method.name", method) - .setStartTimestamp(Instant.now()) - .startSpan(); - - Scope scope = span.makeCurrent(); - - return new SpanScope(span, scope); - } - - @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { - - if (Objects.nonNull(throwable)) { - - String exception = throwable.getClass().getName() - + " " - + "(" - + throwable.getMessage() - + ")"; - - spanScope.span().recordException(throwable); - spanScope.span().setStatus(StatusCode.ERROR, exception); - } - - spanScope.span().end(Instant.now()); - spanScope.scope().close(); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/execute/TrafficHunterAgentExecutableContext.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/execute/TrafficHunterAgentExecutableContext.java deleted file mode 100644 index 2cca5e41..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/context/execute/TrafficHunterAgentExecutableContext.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.context.execute; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.TrafficHunterAgentShutdownHook; -import ygo.traffichunter.agent.engine.context.AgentExecutableContext; -import ygo.traffichunter.agent.engine.context.configuration.ConfigurableContextInitializer; -import ygo.traffichunter.agent.engine.env.ConfigurableEnvironment; -import ygo.traffichunter.agent.event.listener.AgentStateEventListener; -import ygo.traffichunter.agent.event.object.AgentStateEvent; -import ygo.traffichunter.agent.event.store.AgentStateEventStore; - -/** - * The {@code TrafficHunterAgentExecutableContext} class represents the execution context - * for the TrafficHunter Agent. It manages the agent's state, event listeners, environment - * configuration, and shutdown operations. - * - *

Purpose:

- *
    - *
  • Maintains the current state of the agent using an atomic {@link AgentStatus}.
  • - *
  • Registers and manages {@link AgentStateEventListener} instances for state change notifications.
  • - *
  • Integrates a shutdown mechanism using {@link TrafficHunterAgentShutdownHook}.
  • - *
- * - *

Key Features:

- *
    - *
  • {@code init()} - Initializes the agent with the provided environment settings.
  • - *
  • {@code addAgentStateEventListener()} - Adds a listener to observe state changes.
  • - *
  • {@code removeAllAgentStateEventListeners()} - Removes all registered listeners.
  • - *
  • {@code close()} - Safely shuts down the agent using a dedicated shutdown thread.
  • - *
  • {@code setStatus()} - Atomically updates the agent's state and notifies listeners of the change.
  • - *
- * - *

Thread Safety:

- *
    - *
  • The {@code status} is managed using {@link AtomicReference}, ensuring thread-safe updates.
  • - *
  • Shutdown operations are protected by a {@link ReentrantLock} to prevent concurrent execution.
  • - *
  • {@link AtomicBoolean} is used to ensure the shutdown logic executes only once.
  • - *
- * - *

Limitations:

- *
    - *
  • State listeners are invoked sequentially; long-running listeners may delay others.
  • - *
  • Shutdown operations rely on an additional thread, which may cause delays during JVM termination.
  • - *
- * - * @see AgentExecutableContext - * @see AgentStatus - * @see TrafficHunterAgentShutdownHook - * @see ConfigurableContextInitializer - * @see AgentStateEventListener - * @author yungwang-o - * @version 1.0.0 - */ -public class TrafficHunterAgentExecutableContext extends AgentStateEventStore implements AgentExecutableContext { - - private final ConfigurableEnvironment environment; - - private final AtomicReference status = new AtomicReference<>(AgentStatus.INITIALIZED); - - private final TrafficHunterAgentShutdownHook shutdownHook; - - private final ReentrantLock shutdownLock = new ReentrantLock(); - - private final AtomicBoolean isShutdown = new AtomicBoolean(false); - - public TrafficHunterAgentExecutableContext(final ConfigurableEnvironment environment, - final TrafficHunterAgentShutdownHook shutdownHook) { - this.environment = environment; - this.shutdownHook = shutdownHook; - } - - @Override - public void addAgentStateEventListener(final AgentStateEventListener listener) { - super.addAgentStateEventListener(listener); - } - - @Override - public void removeAgentStateEventListener(final AgentStateEventListener listener) { - super.removeAgentStateEventListener(listener); - } - - @Override - public void removeAllAgentStateEventListeners() { - super.removeAll(); - } - - @Override - public ConfigurableContextInitializer init() { - return new ConfigurableContextInitializer(environment); - } - - /** - * Safely shuts down the agent by executing the registered shutdown hooks. - *

Ensures that:

- *
    - *
  • The shutdown hook is executed only once.
  • - *
  • Concurrent shutdown attempts are prevented using a {@link ReentrantLock}.
  • - *
- * If the shutdown hook is not enabled, this method does nothing. - */ - @Override - public void close() { - - if(isStopped()) { - return; - } - - if(this.shutdownHook.isEnabledShutdownHook() && this.isShutdown.compareAndSet(false, true)) { - Thread shutdownHookThread; - shutdownLock.lock(); - try { - shutdownHookThread = new Thread(this.shutdownHook, "TrafficHunterAgentShutdownHook"); - shutdownHookThread.start(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - shutdownLock.unlock(); - } - } - } - - @Override - public ConfigurableEnvironment getEnvironment() { - return environment; - } - - @Override - public AgentStatus getStatus() { - return status.get(); - } - - @Override - public boolean isInit() { - return status.get() == AgentStatus.INITIALIZED; - } - - @Override - public boolean isRunning() { - return status.get() == AgentStatus.RUNNING; - } - - @Override - public boolean isStopped() { - return status.get() == AgentStatus.EXIT; - } - - @Override - public void setStatus(final AgentStatus newStatus) { - AgentStatus agentStatus; - - do { - agentStatus = status.get(); - } while (!status.compareAndSet(agentStatus, newStatus)); - - AgentStateEvent event = new AgentStateEvent(this, agentStatus, newStatus); - - notifyAgentStateChange(event); - } - - private void notifyAgentStateChange(final AgentStateEvent event) { - for(AgentStateEventListener listener : super.getListeners()) { - listener.onEvent(event); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/ConfigurableEnvironment.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/ConfigurableEnvironment.java deleted file mode 100644 index 37ac7974..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/ConfigurableEnvironment.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env; - -import java.io.InputStream; -import ygo.traffichunter.agent.engine.env.yaml.YamlConfigurableEnvironment; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -/** - * The {@code ConfigurableEnvironment} interface defines the contract for loading - * agent configuration properties. It supports loading configuration from - * default or provided input sources. - * - *

Features:

- *
    - *
  • Loads configuration properties into a {@link TrafficHunterAgentProperty} instance.
  • - *
  • Supports default and custom input streams for configuration sources.
  • - *
- * - * @see TrafficHunterAgentProperty - * @see YamlConfigurableEnvironment - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface ConfigurableEnvironment { - - TrafficHunterAgentProperty load(); - - TrafficHunterAgentProperty load(InputStream is); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/Environment.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/Environment.java deleted file mode 100644 index 81a31d9b..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/Environment.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum Environment { - - DEFAULT_PATH("/env/agent-env.yml"), - VERSION("1.0.0"), - SYSTEM_PROFILE("traffichunter.config"), - ; - - private final String env; - - Environment(final String env) { - this.env = env; - } - - public String path() { - return env; - } - - public String version() { - return env; - } - - public String systemProfile() { - return env; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironment.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironment.java deleted file mode 100644 index 09d9a73f..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironment.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml; - -import java.io.InputStream; -import java.util.concurrent.TimeUnit; -import org.yaml.snakeyaml.LoaderOptions; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; -import ygo.traffichunter.agent.TrafficHunterAgent; -import ygo.traffichunter.agent.engine.env.ConfigurableEnvironment; -import ygo.traffichunter.agent.engine.env.yaml.bind.RelaxedBindingUtils; -import ygo.traffichunter.agent.engine.env.yaml.root.RootYamlProperty; -import ygo.traffichunter.agent.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.retry.backoff.BackOffPolicy; -import ygo.traffichunter.retry.backoff.policy.ExponentialBackOffPolicy; -import ygo.traffichunter.util.FileUtils; - -/** - * The {@code YamlConfigurableEnvironment} class implements {@link ConfigurableEnvironment} - * to load configuration properties from a YAML file or input stream. - * - *

Features:

- *
    - *
  • Loads configuration properties from a YAML file or input stream.
  • - *
  • Parses YAML data into a {@link TrafficHunterAgentProperty} instance using - * custom relaxed binding rules.
  • - *
  • Supports a default configuration file or custom file paths.
  • - *
- * - * @see ConfigurableEnvironment - * @see TrafficHunterAgentProperty - * - * @author yungwang-o - * @version 1.0.0 - */ -public class YamlConfigurableEnvironment implements ConfigurableEnvironment { - - private static final String DEFAULT_CONFIG_FILE = "agent-env.yml"; - - private final Yaml yaml; - - private final String path; - - public YamlConfigurableEnvironment(final String path) { - final Constructor constructor = new Constructor(RootYamlProperty.class, new LoaderOptions()); - - constructor.setPropertyUtils(new RelaxedBindingUtils()); - - this.yaml = new Yaml(constructor); - this.path = path; - } - - @Override - public TrafficHunterAgentProperty load() { - final InputStream is = FileUtils.getFile(path); - - final RootYamlProperty root = yaml.load(is); - - return YamlParser.parse(root); - } - - @Override - public TrafficHunterAgentProperty load(final InputStream is) { - - final RootYamlProperty root = yaml.load(is); - - return YamlParser.parse(root); - } - - static class YamlParser { - - private static TrafficHunterAgentProperty parse(final RootYamlProperty root){ - - return TrafficHunterAgent.connect(root.getAgent().getServerUri()) - .name(root.getAgent().getName()) - .targetUri(root.getAgent().getTargetUri()) - .locationJar(root.getAgent().getJar()) - .scheduleInterval(root.getAgent().getInterval()) - .scheduleTimeUnit(TimeUnit.SECONDS) - .faultTolerant() - .retry(root.getAgent().getRetry().getMaxAttempt()) - .backOffPolicy(backOffPolicy(root.getAgent().getRetry().getBackoff())) - .complete(); - } - - private static BackOffPolicy backOffPolicy(BackOffSubProperty backOffSubProperty) { - if(backOffSubProperty == null) { - return ExponentialBackOffPolicy.DEFAULT; - } - - return new ExponentialBackOffPolicy( - backOffSubProperty.getIntervalMillis(), - backOffSubProperty.getMultiplier() - ); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/bind/RelaxedBindingUtils.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/bind/RelaxedBindingUtils.java deleted file mode 100644 index 061272b3..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/bind/RelaxedBindingUtils.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml.bind; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.introspector.Property; -import org.yaml.snakeyaml.introspector.PropertyUtils; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RelaxedBindingUtils extends PropertyUtils { - - private static final Logger log = LoggerFactory.getLogger(RelaxedBindingUtils.class); - - @Override - public Property getProperty(final Class type, final String name) { - return super.getProperty(type, kebabToCamel(name)); - } - - private String kebabToCamel(final String kebab) { - final StringBuilder sb = new StringBuilder(); - - String[] split = kebab.split("-"); - - for(int i = 0; i < split.length; i++) { - if(i == 0) { - sb.append(split[i]); - continue; - } - sb.append(split[i].replaceFirst("^[a-z]", String.valueOf(split[i].charAt(0)).toUpperCase())); - } - - return sb.toString(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/RootYamlProperty.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/RootYamlProperty.java deleted file mode 100644 index dc88bef4..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/RootYamlProperty.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml.root; - -import ygo.traffichunter.agent.engine.env.yaml.root.agent.AgentSubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RootYamlProperty { - - private AgentSubProperty agent; - - public RootYamlProperty() { - } - - public RootYamlProperty(final AgentSubProperty agent) { - this.agent = agent; - } - - public AgentSubProperty getAgent() { - return agent; - } - - public void setAgent(final AgentSubProperty agent) { - this.agent = agent; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/AgentSubProperty.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/AgentSubProperty.java deleted file mode 100644 index 3c464f79..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/AgentSubProperty.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml.root.agent; - -import ygo.traffichunter.agent.engine.env.yaml.root.agent.retry.RetrySubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentSubProperty { - - private String name; - private String jar; - private String serverUri; - private String targetUri; - private int interval; - private RetrySubProperty retry; - - public AgentSubProperty() { - } - - public AgentSubProperty(final String name, final String jar, final String serverUri, final String targetUri, - final int interval, - final RetrySubProperty retry) { - this.name = name; - this.jar = jar; - this.serverUri = serverUri; - this.targetUri = targetUri; - this.interval = interval; - this.retry = retry; - } - - public String getServerUri() { - return serverUri; - } - - public void setServerUri(final String serverUri) { - this.serverUri = serverUri; - } - - public String getTargetUri() { - return targetUri; - } - - public void setTargetUri(final String targetUri) { - this.targetUri = targetUri; - } - - public int getInterval() { - return interval; - } - - public void setInterval(final int interval) { - this.interval = interval; - } - - public RetrySubProperty getRetry() { - return retry; - } - - public void setRetry(final RetrySubProperty retry) { - this.retry = retry; - } - - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; - } - - public String getJar() { - return jar; - } - - public void setJar(final String jar) { - this.jar = jar; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/RetrySubProperty.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/RetrySubProperty.java deleted file mode 100644 index e7949f13..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/RetrySubProperty.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml.root.agent.retry; - -import ygo.traffichunter.agent.engine.env.yaml.root.agent.retry.backoff.BackOffSubProperty; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class RetrySubProperty { - - private int maxAttempt; - private BackOffSubProperty backoff; - - public RetrySubProperty() { - } - - public RetrySubProperty(final int maxAttempt, final BackOffSubProperty backoff) { - this.maxAttempt = maxAttempt; - this.backoff = backoff; - } - - public int getMaxAttempt() { - return maxAttempt; - } - - public void setMaxAttempt(final int maxAttempt) { - this.maxAttempt = maxAttempt; - } - - public BackOffSubProperty getBackoff() { - return backoff; - } - - public void setBackoff(final BackOffSubProperty backoff) { - this.backoff = backoff; - } - - @Override - public String toString() { - return "RetrySubProperty{" + - "maxAttempt=" + maxAttempt + - ", backoff=" + backoff + - '}'; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java deleted file mode 100644 index 5151880d..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.env.yaml.root.agent.retry.backoff; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class BackOffSubProperty { - - private long intervalMillis; - private int multiplier; - - public BackOffSubProperty() { - } - - public BackOffSubProperty(final long intervalMillis, final int multiplier) { - this.intervalMillis = intervalMillis; - this.multiplier = multiplier; - } - - public long getIntervalMillis() { - return intervalMillis; - } - - public void setIntervalMillis(final long intervalMillis) { - this.intervalMillis = intervalMillis; - } - - public int getMultiplier() { - return multiplier; - } - - public void setMultiplier(final int multiplier) { - this.multiplier = multiplier; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/JavaInstrumentAgentMain.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/JavaInstrumentAgentMain.java deleted file mode 100644 index e74b186f..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/JavaInstrumentAgentMain.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.instrument; - -import java.lang.instrument.Instrumentation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.AgentExecutionEngine; -import ygo.traffichunter.agent.engine.env.Environment; -import ygo.traffichunter.agent.engine.instrument.bootstrap.BootState; - -/** - * main. - * @author yungwang-o - * @version 1.0.0 - */ -public class JavaInstrumentAgentMain { - - private static final Logger log = LoggerFactory.getLogger(JavaInstrumentAgentMain.class); - - private static final BootState STATE = new BootState(); - - public static void premain(String agentArgs, Instrumentation inst) { - - final boolean success = STATE.start(); - if(!success) { - log.error("traffic-hunter-agent-bootstrap already started. skipping agent loading."); - return; - } - - AgentExecutionEngine.run(Environment.SYSTEM_PROFILE.systemProfile(), inst); - } - - public static void agentmain(String agentArgs, Instrumentation inst) { - premain(agentArgs, inst); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/annotation/AnnotationPath.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/annotation/AnnotationPath.java deleted file mode 100644 index 7a6f5c75..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/annotation/AnnotationPath.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.instrument.annotation; - -/** - * The {@code AnnotationPath} enum defines commonly used annotation paths - * for Spring-based applications, which are used for bytecode manipulation - * with ByteBuddy. - * - * @author yungwang-o - * @version 1.0.0 - */ -public enum AnnotationPath { - - TRANSACTIONAL("org.springframework.transaction.annotation.Transactional"), - SERVICE("org.springframework.stereotype.Service"), - REPOSITORY("org.springframework.stereotype.Repository"), - REST_CONTROLLER("org.springframework.web.bind.annotation.RestController"), - CONTROLLER("org.springframework.stereotype.Controller"), - ; - - private final String path; - - AnnotationPath(final String path) { - this.path = path; - } - - public String getPath() { - return path; - } - - public static boolean filter(final String path) { - return path.equals("join") || path.equals("wait") || path.equals("notify") || path.equals("notifyAll") || - path.equals("hashcode") || path.equals("equals") || path.equals("toString"); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/bootstrap/BootState.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/bootstrap/BootState.java deleted file mode 100644 index 83363c94..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/bootstrap/BootState.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.instrument.bootstrap; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class BootState { - - private static final Boolean STATE_NONE = false; - private static final Boolean STATE_STARTED = true; - - private final AtomicBoolean state = new AtomicBoolean(STATE_NONE); - - boolean getState() { - return state.get(); - } - - public boolean start() { - return state.compareAndSet(STATE_NONE, STATE_STARTED); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/classloading/THAgentClassLoader.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/classloading/THAgentClassLoader.java deleted file mode 100644 index 4ac75a4a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/classloading/THAgentClassLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.instrument.classloading; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class THAgentClassLoader extends ClassLoader { - - public THAgentClassLoader(final ClassLoader parent) { - super(parent); - } - - @Override - protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { - Class clazz = findLoadedClass(name); - - if(clazz == null) { - try { - clazz = findClass(name); - } catch (ClassNotFoundException ignored) { - } - - if(clazz == null) { - clazz = getParent().loadClass(name); - } - } - if(resolve) { - resolveClass(clazz); - } - return clazz; - } - } - - -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/locator/AgentLocator.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/locator/AgentLocator.java deleted file mode 100644 index cef2a325..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/instrument/locator/AgentLocator.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.instrument.locator; - -import java.io.File; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import ygo.traffichunter.agent.engine.instrument.JavaInstrumentAgentMain; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentLocator { - - public static File getAgentJarFile() throws URISyntaxException { - - ProtectionDomain protectionDomain = JavaInstrumentAgentMain.class.getProtectionDomain(); - CodeSource codeSource = protectionDomain.getCodeSource(); - if(codeSource == null) { - throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain)); - } - - URL location = codeSource.getLocation(); - if(location == null) { - throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource)); - } - - final File agentJar = new File(location.toURI()); - if(agentJar.getName().endsWith(".jar")) { - throw new IllegalStateException("Agent is not a jar file: " + agentJar); - } - - return agentJar.getAbsoluteFile(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/jvm/JVMSelector.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/jvm/JVMSelector.java deleted file mode 100644 index 387a5e66..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/jvm/JVMSelector.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.jvm; - -import com.sun.tools.attach.AttachNotSupportedException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; -import java.io.IOException; -import java.util.Objects; -import java.util.logging.Logger; -import javax.management.remote.JMXServiceURL; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class JVMSelector { - - private static final Logger log = Logger.getLogger(JVMSelector.class.getName()); - - public static JMXServiceURL getVMXServiceUrl(final String targetJVMPath) { - - try { - final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); - - final VirtualMachine vm = VirtualMachine.attach(vmDescriptor.id().trim()); - - final String jmxUrl = vm.startLocalManagementAgent(); - - return new JMXServiceURL(jmxUrl); - } catch (AttachNotSupportedException | IOException e) { - log.warning("Not found jvm service url : " + e.getMessage()); - throw new RuntimeException(e); - } - } - - public static VirtualMachine getVM(final String targetJVMPath) { - - try { - final VirtualMachineDescriptor vmDescriptor = getVirtualMachineDescriptor(targetJVMPath); - - return VirtualMachine.attach(vmDescriptor.id().trim()); - - } catch (AttachNotSupportedException | IOException e) { - log.warning("Not found jvm service url : " + e.getMessage()); - throw new RuntimeException(e); - } - } - - private static VirtualMachineDescriptor getVirtualMachineDescriptor(final String targetJVMPath) { - - return VirtualMachine.list().stream() - .filter(vm -> Objects.equals(vm.displayName(), targetJVMPath)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No virtual machine found")); - } - - public static String displayName(final int selectNumber) { - return VirtualMachine.list().get(selectNumber - 1).displayName(); - } - - public static String JvmId(final int selectNumber) { - return VirtualMachine.list().get(selectNumber - 1).id(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/lifecycle/LifeCycle.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/lifecycle/LifeCycle.java deleted file mode 100644 index 420af671..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/lifecycle/LifeCycle.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.lifecycle; - -import java.time.Duration; -import java.time.Instant; - -/** - * The {@code LifeCycle} abstract class defines a common structure for managing - * the lifecycle of an object, including its start time, end time, and uptime. - * - *

Features:

- *
    - *
  • Automatically initializes the start time when an instance is created.
  • - *
  • Provides abstract methods for retrieving the start time, end time, and uptime.
  • - *
  • Allows subclasses to implement specific behaviors for managing lifecycle information.
  • - *
- * - *

Usage:

- *
{@code
- * public class TaskLifeCycle extends LifeCycle {
- *
- *     @Override
- *     public Instant getStartTime() {
- *         return startTime;
- *     }
- *
- *     @Override
- *     public Instant getEndTime() {
- *         if (endTime == null) {
- *             endTime = Instant.now();
- *         }
- *         return endTime;
- *     }
- *
- *     @Override
- *     public Duration getUpTime() {
- *         return Duration.between(getStartTime(), getEndTime());
- *     }
- * }
- *
- * TaskLifeCycle task = new TaskLifeCycle();
- * System.out.println("Uptime: " + task.getUpTime().toSeconds() + " seconds");
- * }
- * - * @see Instant - * @see Duration - * - * @author yungwang-o - * @version 1.0.0 -*/ -public abstract class LifeCycle { - - protected final Instant startTime; - - protected Instant endTime; - - protected LifeCycle() { - this.startTime = Instant.now(); - } - - public abstract Instant getStartTime(); - - public abstract Instant getEndTime(); - - public abstract Double getUpTime(); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/dbcp/HikariDbcpInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/dbcp/HikariDbcpInfo.java deleted file mode 100644 index e2b0d106..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/dbcp/HikariDbcpInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.dbcp; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record HikariDbcpInfo( - // Connection Status - int activeConnections, // Number of active connections currently in use - int idleConnections, // Number of idle connections in the pool - int totalConnections, // Total number of connections in the pool - int threadsAwaitingConnection // Number of threads waiting for a connection -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/AgentMetadata.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/AgentMetadata.java deleted file mode 100644 index a2eaef3f..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/AgentMetadata.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.metadata; - -import java.time.Instant; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.event.listener.AgentStateEventListener; -import ygo.traffichunter.agent.event.object.AgentStateEvent; - -/** - * The {@code AgentMetadata} record represents metadata for an agent and acts as an - * {@link AgentStateEventListener} to respond to state change events. - * - *

Features:

- *
    - *
  • Stores immutable metadata fields such as agent ID, version, name, and start time.
  • - *
  • Maintains a mutable {@link AtomicReference} for the agent's current status.
  • - *
  • Implements {@link AgentStateEventListener} to react to state change events.
  • - *
  • Overrides {@code equals} and {@code hashCode} to handle mutable status appropriately.
  • - *
- * - * @see AgentStateEventListener - * @see AgentStateEvent - * @see AgentStatus - * @see AtomicReference - * - * @author yungwang-o - * @version 1.0.0 - */ -public record AgentMetadata( - String agentId, - String agentVersion, - String agentName, - Instant startTime, - AtomicReference status -) implements AgentStateEventListener { - - @Override - public void onEvent(final AgentStateEvent event) { - this.setStatus(event.getAfterStatus()); - } - - private void setStatus(final AgentStatus status) { - this.status.set(status); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AgentMetadata that = (AgentMetadata) o; - return Objects.equals(agentId, that.agentId) && - Objects.equals(agentVersion, that.agentVersion) && - Objects.equals(agentName, that.agentName) && - Objects.equals(startTime, that.startTime) && - Objects.equals(status.get(), that.status.get()); - } - - @Override - public int hashCode() { - return Objects.hash(agentId, agentVersion, agentName, startTime, status.get()); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/MetadataWrapper.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/MetadataWrapper.java deleted file mode 100644 index 3f9c04a9..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/metadata/MetadataWrapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.metadata; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record MetadataWrapper(AgentMetadata metadata, D data) { - - public static MetadataWrapper create(AgentMetadata metadata, D data) { - return new MetadataWrapper<>(metadata, data); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/SystemInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/SystemInfo.java deleted file mode 100644 index 9173cae3..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/SystemInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo; - -import java.time.Instant; -import ygo.traffichunter.agent.engine.metric.dbcp.HikariDbcpInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.gc.GarbageCollectionStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.memory.MemoryStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.runtime.RuntimeStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.cpu.CpuStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.thread.ThreadStatusInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.TomcatWebServerInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record SystemInfo( - Instant time, - MemoryStatusInfo memoryStatusInfo, - ThreadStatusInfo threadStatusInfo, - CpuStatusInfo cpuStatusInfo, - GarbageCollectionStatusInfo garbageCollectionStatusInfo, - RuntimeStatusInfo runtimeStatusInfo, - TomcatWebServerInfo tomcatWebServerInfo, - HikariDbcpInfo hikariDbcpInfo -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/cpu/CpuStatusInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/cpu/CpuStatusInfo.java deleted file mode 100644 index 6487bd87..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/cpu/CpuStatusInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.cpu; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record CpuStatusInfo(double systemCpuLoad, double processCpuLoad, long availableProcessors) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/GarbageCollectionStatusInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/GarbageCollectionStatusInfo.java deleted file mode 100644 index 4ddd4248..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/GarbageCollectionStatusInfo.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.gc; - -import java.util.List; -import ygo.traffichunter.agent.engine.metric.systeminfo.gc.collections.GarbageCollectionTime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record GarbageCollectionStatusInfo(List garbageCollectionTimes) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/collections/GarbageCollectionTime.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/collections/GarbageCollectionTime.java deleted file mode 100644 index ef31b571..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/gc/collections/GarbageCollectionTime.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.gc.collections; - -import java.lang.management.GarbageCollectorMXBean; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record GarbageCollectionTime( - long getCollectionCount, - long getCollectionTime -) { - - public GarbageCollectionTime(final GarbageCollectorMXBean mxBean) { - this(mxBean.getCollectionCount(), mxBean.getCollectionTime()); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/memory/MemoryStatusInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/memory/MemoryStatusInfo.java deleted file mode 100644 index d9cc83ec..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/memory/MemoryStatusInfo.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.memory; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record MemoryStatusInfo(MemoryUsage heapMemoryUsage, MemoryUsage nonHeapMemoryUsage) { - - public record MemoryUsage(long init, long used, long committed, long max) {} -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/runtime/RuntimeStatusInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/runtime/RuntimeStatusInfo.java deleted file mode 100644 index c27b202a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/runtime/RuntimeStatusInfo.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.runtime; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record RuntimeStatusInfo( - long getStartTime, - long getUpTime, - String getVmName, - String getVmVersion -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/thread/ThreadStatusInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/thread/ThreadStatusInfo.java deleted file mode 100644 index e6d9521d..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/systeminfo/thread/ThreadStatusInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.systeminfo.thread; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record ThreadStatusInfo( - int threadCount, - int getPeekThreadCount, - long getTotalStartThreadCount -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TraceInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TraceInfo.java deleted file mode 100644 index 8306e62c..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TraceInfo.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.transaction; - -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.time.Duration; -import java.time.Instant; -import java.util.Objects; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TraceInfo( - - String name, - - String traceId, - - String parentSpanId, - - String spanId, - - Instant startTime, - - Instant endTime, - - long duration, - - String exception, - - boolean ended -) { - - public static TraceInfo translate(final SpanData spanData) { - - Instant startTime = Instant.EPOCH.plusNanos(spanData.getStartEpochNanos()); - - Instant endTime = Instant.EPOCH.plusNanos(spanData.getEndEpochNanos()); - - Duration between = Duration.between(startTime, endTime); - - return new TraceInfo( - spanData.getName(), - spanData.getTraceId(), - spanData.getParentSpanId(), - spanData.getSpanId(), - startTime, - endTime, - between.toMillis(), - getException(spanData), - spanData.hasEnded() - ); - } - - private static String getException(final SpanData spanData) { - if(Objects.equals(spanData.getStatus().getStatusCode(), StatusCode.ERROR)) { - return spanData.getStatus().getDescription(); - } - - return "success"; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TransactionInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TransactionInfo.java deleted file mode 100644 index 9ae118cf..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/transaction/TransactionInfo.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.transaction; - -import java.time.Instant; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -@Deprecated(since = "1.0.0") -public record TransactionInfo( - String txName, - Instant startTime, - Instant endTime, - long duration, - String errorMessage, - boolean isSuccess -) { - - public static TransactionInfo create(final String txName, - final Instant startTime, - final Instant endTime, - final long duration, - final String errorMessage, - final boolean isSuccess) { - - return new TransactionInfo(txName, startTime, endTime, duration, errorMessage, isSuccess); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/TomcatWebServerInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/TomcatWebServerInfo.java deleted file mode 100644 index c89d5fe1..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/TomcatWebServerInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.web.tomcat; - -import ygo.traffichunter.agent.engine.metric.web.tomcat.request.TomcatRequestInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.thread.TomcatThreadPoolInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatWebServerInfo( - TomcatThreadPoolInfo tomcatThreadPoolInfo, - TomcatRequestInfo tomcatRequestInfo -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/request/TomcatRequestInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/request/TomcatRequestInfo.java deleted file mode 100644 index c138a435..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/request/TomcatRequestInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.web.tomcat.request; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatRequestInfo( - long requestCount, - long bytesReceived, - long bytesSent, - long processingTime, - long errorCount -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/thread/TomcatThreadPoolInfo.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/thread/TomcatThreadPoolInfo.java deleted file mode 100644 index 28b02808..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/metric/web/tomcat/thread/TomcatThreadPoolInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.metric.web.tomcat.thread; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TomcatThreadPoolInfo( - int maxThreads, - int currentThreads, - int currentThreadsBusy -) { -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/queue/SyncQueue.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/queue/SyncQueue.java deleted file mode 100644 index 65be93d3..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/queue/SyncQueue.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.queue; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import ygo.traffichunter.agent.engine.metric.transaction.TraceInfo; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public enum SyncQueue { - - INSTANCE, - ; - - private final BlockingQueue syncQ = new LinkedBlockingQueue<>(100); - - /** - * this method is non-blocking - * @return success : true, fail : false - */ - public boolean add(final TraceInfo trInfo) { - return syncQ.offer(trInfo); - } - - /** - * this method is blocking - */ - public TraceInfo poll() throws InterruptedException { - return syncQ.take(); - } - - public void removeAll() { - syncQ.clear(); - } - - public int size() { - return syncQ.size(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/MetricSender.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/MetricSender.java deleted file mode 100644 index 2b803cff..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/MetricSender.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.sender; - -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.engine.sender.websocket.AgentSystemMetricSender; -import ygo.traffichunter.agent.engine.sender.websocket.AgentTransactionMetricSender; - -/** - * The {@code MetricSender} interface defines the contract for sending metrics. - * Implementations of this interface are responsible for serializing and sending - * specific types of metrics using a given communication client. - * - * @see AgentTransactionMetricSender - * @see AgentSystemMetricSender - */ -public interface MetricSender { - - void toSend(AgentMetadata metadata); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/manager/MetricSendSessionManager.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/manager/MetricSendSessionManager.java deleted file mode 100644 index 80323202..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/manager/MetricSendSessionManager.java +++ /dev/null @@ -1,183 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.sender.manager; - -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryConfig; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.context.AgentExecutableContext; -import ygo.traffichunter.agent.engine.sender.websocket.AgentSystemMetricSender; -import ygo.traffichunter.agent.engine.sender.websocket.AgentTransactionMetricSender; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.retry.RetryHelper; -import ygo.traffichunter.util.AgentUtil; -import ygo.traffichunter.websocket.MetricWebSocketClient; - -/** - * The {@code MetricSendSessionManager} class manages the session for sending - * transaction and system metrics from the TrafficHunter Agent to a server. - * It handles WebSocket connections, retries, scheduling, and lifecycle management - * of the metric-sending operations. - * - *

Purpose:

- *
    - *
  • Manages WebSocket connections for real-time metric transmission.
  • - *
  • Schedules periodic system metric transmissions using a {@link ScheduledExecutorService}.
  • - *
  • Retries connection attempts in case of failures with a configurable back-off policy.
  • - *
  • Provides lifecycle management for starting and stopping the session.
  • - *
- * - *

Key Features:

- *
    - *
  • {@code run()} - Starts the metric sending session, ensuring retries and scheduling are properly configured.
  • - *
  • {@code close()} - Safely shuts down the session, releasing all resources.
  • - *
  • Integrates with {@link Retry} to handle WebSocket reconnections in case of failures.
  • - *
- * - *

Thread Management:

- *
    - *
  • Uses a {@link ScheduledExecutorService} for scheduling periodic system metrics.
  • - *
  • Uses a {@link ExecutorService} for running transaction metric transmissions.
  • - *
  • Both executors are safely shut down during the {@code close()} method.
  • - *
- * - *

Limitations:

- *
    - *
  • If the agent's context status is already {@code RUNNING}, the session cannot be restarted without stopping it first.
  • - *
  • Retries only handle specific exceptions, as configured in the {@link RetryHelper}.
  • - *
- * - * @see Retry - * @see ScheduledExecutorService - * @see MetricWebSocketClient - * @see TrafficHunterAgentProperty - * @see AgentExecutableContext - * @see AgentMetadata - * @see RetryHelper - * @author yungwang-o - * @version 1.0.0 - */ -public class MetricSendSessionManager { - - private static final Logger log = LoggerFactory.getLogger(MetricSendSessionManager.class); - - private final TrafficHunterAgentProperty property; - - private final AgentTransactionMetricSender transactionMetricSender; - - private final AgentSystemMetricSender systemMetricSender; - - private final ScheduledExecutorService schedule; - - private final ExecutorService executor; - - private final AgentExecutableContext context; - - private final AgentMetadata metadata; - - private final MetricWebSocketClient client; - - public MetricSendSessionManager(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.client = new MetricWebSocketClient(AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), metadata); - this.client.connect(); - this.metadata = metadata; - this.context = context; - this.property = property; - this.transactionMetricSender = new AgentTransactionMetricSender(client); - this.systemMetricSender = new AgentSystemMetricSender(client, property); - this.schedule = Executors.newSingleThreadScheduledExecutor(getThreadFactory("TransactionSystemInfoMetricSender")); - this.executor = Executors.newVirtualThreadPerTaskExecutor(); - } - - public void run() { - - if(context.isStopped()) { - log.error("MetricSendSessionManager is stopped"); - return; - } - - if(context.isRunning()) { - log.error("MetricSendSessionManager is already running"); - return; - } - - log.info("start Metric send!!"); - - final RetryHelper retryHelper = RetryHelper.builder() - .backOffPolicy(property.backOffPolicy()) - .isCheck(true) - .retryName("websocket retry") - .maxAttempts(property.maxAttempt()) - .retryPredicate(throwable -> throwable instanceof IllegalStateException) - .build(); - - RetryConfig retryConfig = retryHelper.configureRetry(); - - Retry retry = Retry.of(retryHelper.getRetryName(), retryConfig); - - retry.getEventPublisher() - .onRetry(event -> { - client.reconnect(); - log.info("{} retry {} attempts...", event.getName(), event.getNumberOfRetryAttempts()); - }); - - context.setStatus(AgentStatus.RUNNING); - - executor.execute(Retry.decorateRunnable(retry, () -> transactionMetricSender.toSend(metadata))); - - schedule.scheduleWithFixedDelay(Retry.decorateRunnable(retry, () -> systemMetricSender.toSend(metadata)), - 0, - property.scheduleInterval(), - property.timeUnit() - ); - } - - public void close() { - log.info("closing MetricSendSessionManager..."); - context.setStatus(AgentStatus.EXIT); - client.close(); - executor.shutdown(); - schedule.shutdown(); - } - - private ThreadFactory getThreadFactory(final String threadName) { - return r -> { - Thread thread = new Thread(r); - thread.setDaemon(true); - thread.setName(threadName); - - return thread; - }; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentSystemMetricSender.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentSystemMetricSender.java deleted file mode 100644 index 0f96fcb6..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentSystemMetricSender.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.collect.MetricCollectSupport; -import ygo.traffichunter.agent.engine.sender.MetricSender; -import ygo.traffichunter.agent.engine.metric.systeminfo.SystemInfo; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.engine.metric.metadata.MetadataWrapper; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; -import ygo.traffichunter.websocket.MetricWebSocketClient; -import ygo.traffichunter.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - * The {@code AgentSystemMetricSender} class is responsible for sending system metrics - * to the server via a WebSocket connection. - * - *

Features:

- *
    - *
  • Collects system metric data from the local environment.
  • - *
  • Wraps the system data with metadata and sends it in a compressed format.
  • - *
  • Uses {@link MetricCollectSupport} for metric collection.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see MetricCollectSupport - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentSystemMetricSender implements MetricSender { - - private static final Logger log = LoggerFactory.getLogger(AgentSystemMetricSender.class); - - private final MetricWebSocketClient client; - - private final MetricCollectSupport metricCollectSupport; - - public AgentSystemMetricSender(final MetricWebSocketClient client, - final TrafficHunterAgentProperty property) { - this.client = client; - this.metricCollectSupport = new MetricCollectSupport(property); - } - - @Override - public void toSend(final AgentMetadata metadata) { - final SystemInfo systemInfo = metricCollectSupport.collect(); - - final MetadataWrapper wrapper = MetadataWrapper.create(metadata, systemInfo); - - client.compressToSend(wrapper, MetricType.SYSTEM_METRIC); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSender.java b/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSender.java deleted file mode 100644 index d067883a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSender.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.engine.metric.metadata.MetadataWrapper; -import ygo.traffichunter.agent.engine.metric.transaction.TraceInfo; -import ygo.traffichunter.agent.engine.queue.SyncQueue; -import ygo.traffichunter.agent.engine.sender.MetricSender; -import ygo.traffichunter.websocket.MetricWebSocketClient; -import ygo.traffichunter.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - *

- * The {@code AgentTransactionMetricSender} class is responsible for sending transaction metrics - * to the server via a WebSocket connection. - *

- * - *

Features:

- *
    - *
  • Continuously retrieves transaction data from a synchronized queue.
  • - *
  • Wraps the transaction data with metadata and sends it in a compressed format.
  • - *
  • Relies on {@link SyncQueue} for thread-safe access to transaction data.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see SyncQueue - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentTransactionMetricSender implements MetricSender { - - public static final Logger log = LoggerFactory.getLogger(AgentTransactionMetricSender.class); - - private final MetricWebSocketClient client; - - public AgentTransactionMetricSender(final MetricWebSocketClient client) { - this.client = client; - } - - @Override - public void toSend(final AgentMetadata metadata) { - - while (!Thread.currentThread().isInterrupted()) { - - try { - - TraceInfo trInfo = SyncQueue.INSTANCE.poll(); - - MetadataWrapper wrapper = MetadataWrapper.create(metadata, trInfo); - - client.compressToSend(wrapper, MetricType.TRANSACTION_METRIC); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } catch (IllegalStateException e) { - log.error("exception while sending transaction metric = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/event/AgentEvent.java b/java-agent/src/main/java/ygo/traffichunter/agent/event/AgentEvent.java deleted file mode 100644 index 220da802..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/event/AgentEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.event; - -import java.time.Instant; -import java.util.EventObject; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public abstract class AgentEvent extends EventObject { - - private final long timestamp; - - public AgentEvent(final Object source) { - super(source); - this.timestamp = System.currentTimeMillis(); - } - - public AgentEvent(final Object source, final Instant instant) { - super(source); - this.timestamp = instant.toEpochMilli(); - } - - public final long getTimestamp() { - return this.timestamp; - } - - public Object getSource() { - return super.getSource(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/event/context/AgentContextStateEventHandler.java b/java-agent/src/main/java/ygo/traffichunter/agent/event/context/AgentContextStateEventHandler.java deleted file mode 100644 index 2a6241c9..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/event/context/AgentContextStateEventHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.event.context; - -import ygo.traffichunter.agent.event.listener.AgentStateEventListener; - -/** - * The {@code AgentContextStateEventHandler} interface defines methods for managing - * {@link AgentStateEventListener} instances. It allows adding, removing, and clearing - * listeners for handling agent state change events. - * - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentContextStateEventHandler { - - void addAgentStateEventListener(final AgentStateEventListener listener); - - void removeAgentStateEventListener(final AgentStateEventListener listener); - - void removeAllAgentStateEventListeners(); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/event/listener/AgentStateEventListener.java b/java-agent/src/main/java/ygo/traffichunter/agent/event/listener/AgentStateEventListener.java deleted file mode 100644 index 31f8eee4..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/event/listener/AgentStateEventListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.event.listener; - -import java.util.EventListener; -import ygo.traffichunter.agent.event.object.AgentStateEvent; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public interface AgentStateEventListener extends EventListener { - - void onEvent(AgentStateEvent event); -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/event/object/AgentStateEvent.java b/java-agent/src/main/java/ygo/traffichunter/agent/event/object/AgentStateEvent.java deleted file mode 100644 index b183878f..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/event/object/AgentStateEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.event.object; - -import java.time.Instant; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.event.AgentEvent; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentStateEvent extends AgentEvent { - - private final AgentStatus beforeStatus; - - private final AgentStatus afterStatus; - - public AgentStateEvent(final Object source, - final AgentStatus beforeStatus, - final AgentStatus afterStatus) { - - super(source); - this.beforeStatus = beforeStatus; - this.afterStatus = afterStatus; - } - - public AgentStateEvent(final Object source, - final Instant instant, - final AgentStatus beforeStatus, - final AgentStatus afterStatus) { - - super(source, instant); - this.beforeStatus = beforeStatus; - this.afterStatus = afterStatus; - } - - public AgentStatus getBeforeStatus() { - return this.beforeStatus; - } - - public AgentStatus getAfterStatus() { - return this.afterStatus; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/event/store/AgentStateEventStore.java b/java-agent/src/main/java/ygo/traffichunter/agent/event/store/AgentStateEventStore.java deleted file mode 100644 index 65ce62c0..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/event/store/AgentStateEventStore.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.event.store; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import ygo.traffichunter.agent.event.listener.AgentStateEventListener; - -/** - * The {@code AgentStateEventStore} class manages a collection of {@link AgentStateEventListener} instances. - * It provides methods to add, remove, and retrieve listeners, enabling event-driven state management - * for the TrafficHunter Agent. - * - *

Purpose:

- *
    - *
  • Stores event listeners for agent state changes.
  • - *
  • Optimized for frequent reads and infrequent writes using {@link CopyOnWriteArrayList}.
  • - *
- * - * @see AgentStateEventListener - * @see CopyOnWriteArrayList - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentStateEventStore { - - private final List listeners = new CopyOnWriteArrayList<>(); - - protected void addAgentStateEventListener(final AgentStateEventListener listener) { - listeners.add(listener); - } - - protected void removeAgentStateEventListener(final AgentStateEventListener listener) { - if(listeners.isEmpty()) { - throw new IllegalArgumentException("listener is empty"); - } - - listeners.remove(listener); - } - - public List getListeners() { - return listeners; - } - - protected void removeAll() { - listeners.clear(); - } - - public int listenerSize() { - return listeners.size(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/agent/property/TrafficHunterAgentProperty.java b/java-agent/src/main/java/ygo/traffichunter/agent/property/TrafficHunterAgentProperty.java deleted file mode 100644 index ee44ffcb..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/agent/property/TrafficHunterAgentProperty.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.agent.property; - -import java.util.concurrent.TimeUnit; -import ygo.traffichunter.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public record TrafficHunterAgentProperty( - String name, - String targetUri, - String jar, - int scheduleInterval, - String serverUri, - TimeUnit timeUnit, - int maxAttempt, - BackOffPolicy backOffPolicy -) { - - public TrafficHunterAgentProperty(final String name, - final String targetUri, - final String jar, - final int scheduleInterval, - final String serverUri, - final TimeUnit timeUnit, - final int maxAttempt, - final BackOffPolicy backOffPolicy) { - - this.name = name; - this.targetUri = targetUri; - this.jar = jar; - this.scheduleInterval = scheduleInterval; - this.serverUri = serverUri; - this.timeUnit = timeUnit; - this.maxAttempt = maxAttempt; - this.backOffPolicy = backOffPolicy; - } - - public TrafficHunterAgentProperty(final String name, - final String targetUri, - final String jar, - final int scheduleInterval, - final String serverUri, - final TimeUnit timeUnit) { - - this(name, targetUri, jar, scheduleInterval, serverUri, timeUnit, 0, null); - } - - @Override - public String toString() { - return "TrafficHunterAgentProperty{" + - "name='" + name + '\'' + - ", targetUri='" + targetUri + '\'' + - ", jar=" + jar + - ", scheduleInterval=" + scheduleInterval + - ", serverUri=" + serverUri + - ", timeUnit=" + timeUnit + - ", maxAttempt=" + maxAttempt + - ", backOffPolicy=" + backOffPolicy.toString() + - '}'; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/http/HttpBuilder.java b/java-agent/src/main/java/ygo/traffichunter/http/HttpBuilder.java deleted file mode 100644 index ced5bab2..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/http/HttpBuilder.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.http; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpClient.Version; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; -import java.time.Duration; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -@Deprecated(since = "1.0.0") -public class HttpBuilder { - - private final URI uri; - - private final ObjectMapper objectMapper; - - private Object obj; - - private String headerKey; - - private String headerValue; - - private Duration timeout = Duration.ofSeconds(10); - - private HttpBuilder(final URI uri) { - this.uri = uri; - this.objectMapper = new ObjectMapper(); - this.objectMapper.registerModule(new JavaTimeModule()); - } - - public static HttpBuilder newBuilder(final URI uri) { - return new HttpBuilder(uri); - } - - public HttpBuilder header(final String key, final String value) { - this.headerKey = key; - this.headerValue = value; - - return this; - } - - public HttpBuilder timeOut(final Duration timeout) { - this.timeout = timeout; - - return this; - } - - public HttpBuilder request(final Object obj) { - this.obj = obj; - - return this; - } - - public HttpResponse build() throws Exception { - try (final HttpClient client = HttpClient.newBuilder() - .version(Version.HTTP_1_1) - .connectTimeout(timeout) - .build()) { - - final HttpRequest request = HttpRequest.newBuilder() - .uri(uri) - .version(Version.HTTP_1_1) - .header(headerKey, headerValue) - .timeout(timeout) - .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(obj))) - .build(); - - return client.send(request, BodyHandlers.ofString()); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/http/status/HttpStatus.java b/java-agent/src/main/java/ygo/traffichunter/http/status/HttpStatus.java deleted file mode 100644 index fa6f781a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/http/status/HttpStatus.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.http.status; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -@Deprecated(since = "1.0.0") -public enum HttpStatus { - - OK(200, "request ok!!"), - NOT_FOUND(404, "not found!!"), - INTERNAL_SERVER_ERROR(500, "internal server error!!"), - BAD_REQUEST(400, "bad request!!"), - ; - - private final int statusCode; - private final String message; - - HttpStatus(final int statusCode, final String message) { - this.statusCode = statusCode; - this.message = message; - } - - public int getStatusCode() { - return statusCode; - } - - public String getMessage() { - return message; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/retry/RetryHelper.java b/java-agent/src/main/java/ygo/traffichunter/retry/RetryHelper.java deleted file mode 100644 index f014b35c..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/retry/RetryHelper.java +++ /dev/null @@ -1,169 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.retry; - -import io.github.resilience4j.core.IntervalFunction; -import io.github.resilience4j.retry.RetryConfig; -import java.util.function.Predicate; -import java.util.logging.Logger; -import ygo.traffichunter.retry.backoff.BackOffPolicy; - -/** - * The {@code RetryHelper} class simplifies the configuration of retry logic, - * including backoff policies, maximum attempts, and exception handling. - * - *

Features:

- *
    - *
  • Provides a fluent {@code Builder} for configuring retry parameters.
  • - *
  • Supports customizable backoff policies via {@link BackOffPolicy}.
  • - *
  • Configures exception-based retry logic with a {@link Predicate}.
  • - *
  • Generates a {@link RetryConfig} compatible with retry libraries.
  • - *
- * - *

Example:

- *
{@code
- * RetryHelper retryHelper = RetryHelper.builder()
- *     .backOffPolicy(new BackOffPolicy(1000, 2))
- *     .maxAttempts(5)
- *     .retryPredicate(e -> e instanceof IllegalStateException)
- *     .retryName("SampleRetry")
- *     .isCheck(true)
- *     .build();
- *
- * RetryConfig retryConfig = retryHelper.configureRetry();
- * }
- * - *

Thread Safety:

- *
    - *
  • This class is immutable and thread-safe once built.
  • - *
- * - * @see RetryConfig - * @see BackOffPolicy - * - * @author yungwang-o - * @version 1.0.0 - */ -public class RetryHelper { - - private static final Logger log = Logger.getLogger(RetryHelper.class.getName()); - - private final BackOffPolicy backOffPolicy; - - private final int maxAttempts; - - private final Predicate retryPredicate; - - private final String retryName; - - private final boolean isCheck; - - private RetryHelper(final Builder builder) { - this.backOffPolicy = builder.backOffPolicy; - this.maxAttempts = builder.maxAttempts; - this.retryPredicate = builder.retryPredicate; - this.retryName = builder.retryName; - this.isCheck = builder.isCheck; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private BackOffPolicy backOffPolicy; - - private int maxAttempts; - - private Predicate retryPredicate; - - private String retryName; - - private boolean isCheck; - - public Builder() { - } - - public Builder backOffPolicy(final BackOffPolicy backOffPolicy) { - this.backOffPolicy = backOffPolicy; - return this; - } - - public Builder maxAttempts(final int maxAttempts) { - this.maxAttempts = maxAttempts; - return this; - } - - public Builder retryPredicate(final Predicate retryPredicate) { - this.retryPredicate = retryPredicate; - return this; - } - - public Builder retryName(final String retryName) { - this.retryName = retryName; - return this; - } - - public Builder isCheck(final boolean isCheck) { - this.isCheck = isCheck; - return this; - } - - public RetryHelper build() { - return new RetryHelper(this); - } - } - - public BackOffPolicy getBackOffPolicy() { - return backOffPolicy; - } - - public int getMaxAttempts() { - return maxAttempts; - } - - public Predicate getRetryPredicate() { - return retryPredicate; - } - - public String getRetryName() { - return retryName; - } - - public boolean isCheck() { - return isCheck; - } - - public RetryConfig configureRetry() { - return RetryConfig.custom() - .maxAttempts(maxAttempts) - .retryOnException(retryPredicate) - .failAfterMaxAttempts(isCheck) - .intervalFunction(IntervalFunction.ofExponentialRandomBackoff( - backOffPolicy.getIntervalMillis(), - backOffPolicy.getMultiplier() - )).build(); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/BackOffPolicy.java b/java-agent/src/main/java/ygo/traffichunter/retry/backoff/BackOffPolicy.java deleted file mode 100644 index 92f30012..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/BackOffPolicy.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.retry.backoff; - -import ygo.traffichunter.retry.backoff.policy.ExponentialBackOffPolicy; -import ygo.traffichunter.retry.backoff.policy.FixedBackOffPolicy; - -/** - * The {@code BackOffPolicy} class serves as the base class for defining backoff strategies - * used in retry mechanisms. It provides common properties such as the interval and multiplier, - * which can be extended for specific backoff behaviors. - * - *

Subclasses:

- *
    - *
  • {@link ExponentialBackOffPolicy}: Implements exponential backoff strategy.
  • - *
  • {@link FixedBackOffPolicy}: Implements fixed interval backoff strategy.
  • - *
- * - * @author yungwang-o - * @version 1.0.0 - */ -public class BackOffPolicy { - - private final long intervalMillis; - private final int multiplier; - - public BackOffPolicy(final long intervalMillis, final int multiplier) { - this.intervalMillis = intervalMillis; - this.multiplier = multiplier; - } - - public long getIntervalMillis() { - return intervalMillis; - } - - public int getMultiplier() { - return multiplier; - } - - @Override - public String toString() { - return "BackOffPolicy{" + - "intervalMillis=" + intervalMillis + - ", multiplier=" + multiplier + - '}'; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/ExponentialBackOffPolicy.java b/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/ExponentialBackOffPolicy.java deleted file mode 100644 index 335ef1a7..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/ExponentialBackOffPolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.retry.backoff.policy; - -import ygo.traffichunter.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class ExponentialBackOffPolicy extends BackOffPolicy { - - public static final ExponentialBackOffPolicy DEFAULT = new ExponentialBackOffPolicy(1000, 2); - - public ExponentialBackOffPolicy(final long intervalMillis, final int multiplier) { - super(intervalMillis, multiplier); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/FixedBackOffPolicy.java b/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/FixedBackOffPolicy.java deleted file mode 100644 index ecd7bc46..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/retry/backoff/policy/FixedBackOffPolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.retry.backoff.policy; - -import ygo.traffichunter.retry.backoff.BackOffPolicy; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class FixedBackOffPolicy extends BackOffPolicy { - - public static final FixedBackOffPolicy DEFAULT = new FixedBackOffPolicy(1000); - - public FixedBackOffPolicy(final long intervalMillis) { - super(intervalMillis, 1); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java deleted file mode 100644 index 8caf2ed5..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceExporter.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.trace.opentelemetry; - -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.util.Collection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.metric.transaction.TraceInfo; -import ygo.traffichunter.agent.engine.queue.SyncQueue; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TraceExporter implements SpanExporter { - - private static final Logger log = LoggerFactory.getLogger(TraceExporter.class); - - private volatile boolean isShutdown = false; - - @Override - public CompletableResultCode export(final Collection spans) { - - if(isShutdown) { - return CompletableResultCode.ofFailure(); - } - - try { - spans.stream() - .map(TraceInfo::translate) - .forEach(SyncQueue.INSTANCE::add); - - return CompletableResultCode.ofSuccess(); - } catch (RuntimeException e) { - return CompletableResultCode.ofFailure(); - } - } - - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - - isShutdown = true; - - try { - return CompletableResultCode.ofSuccess(); - } catch (Exception e) { - return CompletableResultCode.ofFailure(); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java b/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java deleted file mode 100644 index f5acdb32..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/trace/opentelemetry/TraceManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.trace.opentelemetry; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Scope; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.trace.IdGenerator; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import java.util.Objects; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class TraceManager { - - private static final String INSTRUMENTATION_SCOPE_NAME = "instrument-transaction-scope"; - - private static volatile SdkTracerProvider provider; - - private static volatile OpenTelemetrySdk openTelemetrySdk; - - public static Tracer configure(final SpanExporter exporter) { - - provider = SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(exporter)) - .setIdGenerator(IdGenerator.random()) - .setSampler(Sampler.alwaysOn()) - .build(); - - openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(provider) - .build(); - - return openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME); - } - - public static void close() { - if (Objects.nonNull(provider) && Objects.nonNull(openTelemetrySdk)) { - provider.close(); - openTelemetrySdk.close(); - } - } - - public record SpanScope(Span span, Scope scope) { } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/util/AgentUtil.java b/java-agent/src/main/java/ygo/traffichunter/util/AgentUtil.java deleted file mode 100644 index c6f9c523..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/util/AgentUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.util; - -import java.net.URI; - -/** - * The {@code AgentUtil} enum provides utility methods for constructing - * WebSocket and HTTP URLs for the TrafficHunter Agent. It also includes - * methods for validating and formatting server addresses. - * - * @author yungwang-o - * @version 1.0.0 -*/ -public enum AgentUtil { - WEBSOCKET_URL("ws://%s/traffic-hunter/tx"), - HTTP_URL("http://%s/traffic-hunter"), - ; - - private static final String pattern = "^(localhost|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):\\d{1,5}$"; - - private final String url; - - AgentUtil(final String url) { - this.url = url; - } - - public static boolean isAddr(final String serverUrl) { - return serverUrl.matches(pattern); - } - - public String getUrl(final String serverUrl) { - return String.format(url, serverUrl); - } - - public URI getUri(final String serverUrl) { - return URI.create(String.format(url, serverUrl)); - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/util/FileUtils.java b/java-agent/src/main/java/ygo/traffichunter/util/FileUtils.java deleted file mode 100644 index 4adbf04a..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/util/FileUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.util; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class FileUtils { - - private static final Logger log = LoggerFactory.getLogger(FileUtils.class); - - public static FileInputStream getFile(final String path) { - try { - return new FileInputStream(path); - } catch (FileNotFoundException e) { - log.error("Could not open file '{}'", path, e); - throw new RuntimeException(e); - } - } - - -} diff --git a/java-agent/src/main/java/ygo/traffichunter/util/UUIDGenerator.java b/java-agent/src/main/java/ygo/traffichunter/util/UUIDGenerator.java deleted file mode 100644 index b7ae37b5..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/util/UUIDGenerator.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.util; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author yungwang-o - * @version 1.0.0 - */ -public class UUIDGenerator { - - private static final Logger log = LoggerFactory.getLogger(UUIDGenerator.class); - - public static String generate(final String agentName) { - Path path = Paths.get(getPath(agentName)); - - if(!Files.exists(path.getParent())) { - try { - Files.createDirectories(path.getParent()); - } catch (IOException e) { - log.error("Failed to create directory = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - if(Files.exists(path)) { - try { - return Files.readString(path).trim(); - } catch (IOException e) { - log.error("Failed to read file = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - final String uuid = UUID.randomUUID().toString(); - - try { - Files.writeString(path, uuid); - return uuid; - } catch (IOException e) { - log.error("Failed to write file = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - - private static String getPath(final String agentName) { - return System.getProperty("user.home") - + "/traffic-hunter" - + "/key" - + "/" - + agentName - + "_" - + "agent_id.txt"; - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/websocket/MetricWebSocketClient.java b/java-agent/src/main/java/ygo/traffichunter/websocket/MetricWebSocketClient.java deleted file mode 100644 index a8727576..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/websocket/MetricWebSocketClient.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.websocket; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.net.URI; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.websocket.converter.SerializationByteArrayConverter; -import ygo.traffichunter.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - * The {@code MetricWebSocketClient} class extends {@link WebSocketClient} - * to provide specialized functionality for sending serialized metrics over a WebSocket connection. - * - *

Features:

- *
    - *
  • Automatically sends metadata when the connection is opened.
  • - *
  • Supports sending metrics as JSON or compressed binary data.
  • - *
  • Provides utility methods to check and manage WebSocket connection state.
  • - *
- * - * @see WebSocketClient - * @see SerializationByteArrayConverter - * @see AgentMetadata - * @see MetricType - * - * @author yungwang-o - * @version 1.0.0 - */ -public class MetricWebSocketClient extends WebSocketClient { - - private static final Logger log = LoggerFactory.getLogger(MetricWebSocketClient.class); - - private final SerializationByteArrayConverter converter; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - private final AgentMetadata metadata; - - public MetricWebSocketClient(final URI serverUri, final AgentMetadata metadata) { - super(serverUri); - this.metadata = metadata; - this.objectMapper - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .registerModule(new JavaTimeModule()); - this.converter = new SerializationByteArrayConverter(objectMapper); - } - - @Override - public void onOpen(final ServerHandshake serverHandshake) { - log.info("websocket client opened"); - this.toSend(metadata); - } - - @Override - public void onMessage(final String s) { - log.info("websocket client received = {}", s); - } - - - @Override - public void onClose(final int i, final String s, final boolean b) { - log.info("websocket client closed"); - } - - @Override - public void onError(final Exception e) { - log.error("websocket client error = {}", e.getMessage()); - } - - public boolean isConnected() { - if(isOpen()) { - return true; - } - - try { - return super.connectBlocking(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return false; - } - - public boolean isConnected(final long timeOut, final TimeUnit timeUnit) { - if(isOpen()) { - return true; - } - - try { - return super.connectBlocking(timeOut, timeUnit); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return false; - } - - public void toSend(final M metric) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - try { - String s = objectMapper.writeValueAsString(metric); - this.send(s); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public void compressToSend(final M metric, final MetricType metricType) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - byte[] transform = converter.transform(metric, metricType); - - this.send(transform); - } - - public void toSend(final List metrics) { - if(!isOpen()) { - throw new IllegalStateException("WebSocket client is closed"); - } - - try { - this.send(objectMapper.writeValueAsString(metrics)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/java-agent/src/main/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverter.java b/java-agent/src/main/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverter.java deleted file mode 100644 index dae3bb32..00000000 --- a/java-agent/src/main/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverter.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package ygo.traffichunter.websocket.converter; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import ygo.traffichunter.agent.engine.metric.metadata.MetadataWrapper; - -/** - * The {@code SerializationByteArrayConverter} class provides utility methods for - * serializing objects into compressed byte arrays and deserializing compressed byte arrays - * back into objects. This is useful for efficient data transmission or storage. - * - *

Key Features:

- *
    - *
  • Serializes objects to compressed byte arrays with a metric type identifier.
  • - *
  • Deserializes compressed byte arrays back into objects of specified types.
  • - *
  • Supports gzip compression for optimized size reduction.
  • - *
- * - *

Usage:

- *
    - *
  • {@code transform(Object, MetricType)}: Converts an object to a compressed byte array.
  • - *
  • {@code inverseTransform(byte[], TypeReference)}: Converts a compressed byte array back into an object.
  • - *
- * - * @see ObjectMapper - * @see GZIPOutputStream - * @see GZIPInputStream - * @author yungwang-o - * @version 1.0.0 - */ -public class SerializationByteArrayConverter { - - private final ObjectMapper objectMapper; - - public SerializationByteArrayConverter(final ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - public byte[] transform(final Object obj, final MetricType metricType) { - byte[] data = serialize(obj); - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){ - - try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) { - gzipOutputStream.write(data); - } - - byte[] compressByteArray = baos.toByteArray(); - byte[] result = new byte[baos.toByteArray().length + 1]; - - result[0] = metricType.value; - - System.arraycopy(compressByteArray, 0, result, 1, compressByteArray.length); - - return result; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public MetadataWrapper inverseTransform(final byte[] data, - final TypeReference> typeReference) { - - byte[] copy = new byte[data.length - 1]; - - System.arraycopy(data, 1, copy, 0, copy.length); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - try (GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(copy))) { - byte[] result = new byte[2048]; - - int len; - while ((len = gzipInputStream.read(result)) != -1) { - baos.write(result, 0, len); - } - - byte[] byteArray = baos.toByteArray(); - - return objectMapper.readValue(byteArray, typeReference); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private byte[] serialize(final Object object) { - try { - return objectMapper.writeValueAsBytes(object); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public enum MetricType { - SYSTEM_METRIC((byte) 1), - TRANSACTION_METRIC((byte) 2), - ; - - private final byte value; - - MetricType(final byte value) { - this.value = value; - } - } -} diff --git a/java-agent/src/main/resources/agent-banner.txt b/java-agent/src/main/resources/agent-banner.txt deleted file mode 100644 index adb4b4ec..00000000 --- a/java-agent/src/main/resources/agent-banner.txt +++ /dev/null @@ -1,11 +0,0 @@ -_____ ___________________ ______ _____ -__ /_____________ ___ __/__ __/__(_)______ ___ /_____ __________ /_____________ -_ __/_ ___/ __ `/_ /_ __ /_ __ /_ ___/ __ __ \ / / /_ __ \ __/ _ \_ ___/ -/ /_ _ / / /_/ /_ __/ _ __/ _ / / /__ _ / / / /_/ /_ / / / /_ / __/ / -\__/ /_/ \__,_/ /_/ /_/ /_/ \___/ /_/ /_/\__,_/ /_/ /_/\__/ \___//_/ - -:: Traffic Hunter ${version} Ver :: -:: Made by ygo :: -:: Running on Java ${java.version} :: -:: JDK Information ${jdk} ${java.specification} :: -:: Started on ${time} :: \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/AbstractTest.java b/java-agent/src/test/java/ygo/AbstractTest.java deleted file mode 100644 index 7b101de8..00000000 --- a/java-agent/src/test/java/ygo/AbstractTest.java +++ /dev/null @@ -1,8 +0,0 @@ -package ygo; - -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; - -@DisplayNameGeneration(ReplaceUnderscores.class) -public abstract class AbstractTest { -} diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/TrafficHunterAgentTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/TrafficHunterAgentTest.java deleted file mode 100644 index 4072e5b6..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/TrafficHunterAgentTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package ygo.traffichunter.agent; - -import javax.management.MBeanServerConnection; -import javax.management.ObjectName; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.engine.jvm.JVMSelector; - -class TrafficHunterAgentTest extends AbstractTest { - - @Test - @Disabled - void 타겟_jvm_actuator를_에이전트_jmx로_연결하여_메트릭을_수집한다() throws Exception { - // given - JMXServiceURL jmxUrl = JVMSelector.getVMXServiceUrl("ygo.testapp.TestAppApplication"); - - // when - JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl); - - MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection(); - - ObjectName objectName = new ObjectName("org.springframework.boot:type=Endpoint,name=Metrics"); - - Object health = mbsc.invoke(objectName, "metric", new Object[] {"jvm.memory.used"}, new String[] {String.class.getName()}); - - // then - System.out.println(health); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgentTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgentTest.java deleted file mode 100644 index d0ef89c1..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/child/FaultTolerantTrafficHunterAgentTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package ygo.traffichunter.agent.child; - -import org.junit.jupiter.api.Test; - -class FaultTolerantTrafficHunterAgentTest { - - @Test - void 웹소켓_재_연결을_테스트한다() { - // given - - // when - - // then - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/engine/classloader/ClassLoaderTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/engine/classloader/ClassLoaderTest.java deleted file mode 100644 index cf5807f5..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/engine/classloader/ClassLoaderTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package ygo.traffichunter.agent.engine.classloader; - -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.engine.queue.SyncQueue; - -public class ClassLoaderTest extends AbstractTest { - - @Test - void Sync_Queue의_클래스로더를_확인한다() { - - System.out.println(SyncQueue.class.getClassLoader().getName()); - System.out.println(SyncQueue.class.getClassLoader().getParent().getName()); - } -} diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializerTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializerTest.java deleted file mode 100644 index bcd404ba..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/engine/context/configuration/ConfigurableContextInitializerTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package ygo.traffichunter.agent.engine.context.configuration; - -import static org.junit.jupiter.api.Assertions.*; - -import java.nio.file.Paths; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.engine.env.yaml.YamlConfigurableEnvironment; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -class ConfigurableContextInitializerTest extends AbstractTest { - - @Test - void 설정_파일을_초기화한다() { - // given - ConfigurableContextInitializer initializer = - new ConfigurableContextInitializer(new YamlConfigurableEnvironment("/Users/yungwang-o/Documents/agent-env.yml")); - - // when - TrafficHunterAgentProperty property = initializer.property(); - - // then - Assertions.assertNotNull(property); - Assertions.assertEquals(property.targetUri(), "localhost:8080"); - - System.out.println(property); - } - - @Test - void jar_파일의_절대_경로를_반환한다() { - String jar = "build/libs/java-agent-0.0.1-SNAPSHOT-all.jar"; - - System.out.println(Paths.get(jar).toAbsolutePath()); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironmentTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironmentTest.java deleted file mode 100644 index 609f29b8..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/engine/env/yaml/YamlConfigurableEnvironmentTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package ygo.traffichunter.agent.engine.env.yaml; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import ygo.AbstractTest; -import ygo.traffichunter.agent.property.TrafficHunterAgentProperty; - -class YamlConfigurableEnvironmentTest extends AbstractTest { - - @Test - void yaml_파일을_제대로_파싱하는지_확인한다() { - YamlConfigurableEnvironment environment = new YamlConfigurableEnvironment("/Users/yungwang-o/Documents/agent-env.yml"); - - TrafficHunterAgentProperty load = environment.load(); - - System.out.println(load); - } - - @ParameterizedTest - @CsvSource({"max-attempt, maxAttempt", "agent, agent", "aaa, aaa"}) - void 케밥_케이스를_카멜_케이스로_변경(String kebab, String compare) { - - String[] split = kebab.split("-"); - - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < split.length; i++) { - if(i == 0) { - sb.append(split[i]); - continue; - } - sb.append(split[i].replaceFirst("^[a-z]", String.valueOf(split[i].charAt(0)).toUpperCase())); - } - - Assertions.assertEquals(compare, sb.toString()); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSenderTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSenderTest.java deleted file mode 100644 index d5baf67f..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/engine/sender/websocket/AgentTransactionMetricSenderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package ygo.traffichunter.agent.engine.sender.websocket; - -import static org.junit.jupiter.api.Assertions.*; - -import java.net.URI; -import java.util.concurrent.TimeUnit; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.util.AgentUtil; - -class AgentTransactionMetricSenderTest extends AbstractTest { - - @Test - @Disabled - void 웹소켓_송신을_확인한다() throws InterruptedException { - // given - WebSocketClient client = new WebSocketClient(URI.create(AgentUtil.WEBSOCKET_URL.getUrl("localhost:9100"))){ - @Override - public void onOpen(final ServerHandshake handshakedata) { - System.out.println(handshakedata.getHttpStatusMessage()); - System.out.println("connection opened"); - } - - @Override - public void onMessage(final String message) { - System.out.println("received message: " + message); - } - - @Override - public void onClose(final int code, final String reason, final boolean remote) { - System.out.println(code + " " + reason + " " + remote); - System.out.println("connection closed"); - } - - @Override - public void onError(final Exception ex) { - System.out.println("error = " + ex.getMessage()); - } - }; - - // when - client.connectBlocking(3, TimeUnit.SECONDS); - - Assertions.assertTrue(client.isOpen()); - - client.send("hello"); - // then - client.close(); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/agent/tracking/TransactionTrackingTest.java b/java-agent/src/test/java/ygo/traffichunter/agent/tracking/TransactionTrackingTest.java deleted file mode 100644 index 54e29b5b..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/agent/tracking/TransactionTrackingTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package ygo.traffichunter.agent.tracking; - -import com.sun.tools.attach.VirtualMachine; -import java.nio.file.Paths; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.engine.jvm.JVMSelector; - -class TransactionTrackingTest extends AbstractTest { - - @Test - @Disabled - void 타겟_JVM의_트랜잭션을_확인한다() throws Exception { - // given - VirtualMachine vm = JVMSelector.getVM("ygo.testapp.TestAppApplication"); - - // when - String agentPath = Paths.get("build/libs/java-agent-0.0.1-SNAPSHOT-all.jar") - .toAbsolutePath() - .toString(); - - vm.loadAgent(agentPath); - - // then - } -} diff --git a/java-agent/src/test/java/ygo/traffichunter/banner/AsciiBannerTest.java b/java-agent/src/test/java/ygo/traffichunter/banner/AsciiBannerTest.java deleted file mode 100644 index 760c1a77..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/banner/AsciiBannerTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package ygo.traffichunter.banner; - -import java.time.Instant; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.banner.AsciiBanner; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; - -class AsciiBannerTest extends AbstractTest { - - @Test - void 배너_프린트를_테스트한다() { - AsciiBanner asciiBanner = new AsciiBanner(); - - asciiBanner.print(new AgentMetadata("1", "1", "1", Instant.now(), new AtomicReference<>())); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/engine/AgentExecutionEngineTest.java b/java-agent/src/test/java/ygo/traffichunter/engine/AgentExecutionEngineTest.java deleted file mode 100644 index c26d0aad..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/engine/AgentExecutionEngineTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package ygo.traffichunter.engine; - -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; - -class AgentExecutionEngineTest extends AbstractTest { - - @Test - void 에이전트_엔진의_JMX_커넥터를_테스트한다() { - // given - - // when - - // then - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/http/HttpBuilderTest.java b/java-agent/src/test/java/ygo/traffichunter/http/HttpBuilderTest.java deleted file mode 100644 index 6d7d3b51..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/http/HttpBuilderTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package ygo.traffichunter.http; - -import static org.junit.jupiter.api.Assertions.*; - -import java.net.URI; -import java.net.http.HttpResponse; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import ygo.traffichunter.agent.engine.collect.web.tomcat.TomcatMetricCollector; -import ygo.traffichunter.agent.engine.metric.dbcp.HikariDbcpInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.SystemInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.cpu.CpuStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.gc.GarbageCollectionStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.memory.MemoryStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.memory.MemoryStatusInfo.MemoryUsage; -import ygo.traffichunter.agent.engine.metric.systeminfo.runtime.RuntimeStatusInfo; -import ygo.traffichunter.agent.engine.metric.systeminfo.thread.ThreadStatusInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.TomcatWebServerInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.request.TomcatRequestInfo; -import ygo.traffichunter.agent.engine.metric.web.tomcat.thread.TomcatThreadPoolInfo; - -class HttpBuilderTest { - - @Test - @Disabled - void 서버로_잘_송신이_되는지_확인한다() throws Exception { - // given - SystemInfo systemInfo = new SystemInfo( - Instant.now(), - new MemoryStatusInfo(new MemoryUsage(1, 1, 1, 1), new MemoryUsage(1,1, 1, 1)), - new ThreadStatusInfo(1,1, 1), - new CpuStatusInfo(1, 1, 1), - new GarbageCollectionStatusInfo(List.of()), - new RuntimeStatusInfo(1, 1, "", ""), - new TomcatWebServerInfo( - new TomcatThreadPoolInfo(1,1,1), - new TomcatRequestInfo(1, 1,1,1,1) - ), - new HikariDbcpInfo(1,1,1,1) - ); - - // when - HttpResponse httpResponse = HttpBuilder.newBuilder(URI.create("http://localhost:9100/traffic-hunter")) - .header("Content-Type", "application/json") - .timeOut(Duration.ofSeconds(10)) - .request(systemInfo) - .build(); - - // then - assertEquals(httpResponse.statusCode(), 200); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/util/AgentUtilTest.java b/java-agent/src/test/java/ygo/traffichunter/util/AgentUtilTest.java deleted file mode 100644 index 8b1290e8..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/util/AgentUtilTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package ygo.traffichunter.util; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import ygo.AbstractTest; - -class AgentUtilTest extends AbstractTest { - - @ParameterizedTest - @ValueSource(strings = { - "127.0.0.1:8080", - "localhost:7000", - "999.999.999.999:56646", - "192.168.0.1:9000" - }) - void ip_port_형식이_맞는지_확인한다_다만_localhost는_허용한다(final String addr) { - // given - - // when - boolean isAddr = AgentUtil.isAddr(addr); - - // then - assertTrue(isAddr); - } - - @ParameterizedTest - @ValueSource(strings = { - "127.0.1:8080", - "localhost:700000", - "999.999.999.999.999:56646", - "192.168.099999.10000:9000", - "", - ":8080", - "addr:9000" - }) - void 잘못된_ip_port_형식이_들어오는_경우를_확인한다(final String addr) { - // given - - // when - boolean isAddr = AgentUtil.isAddr(addr); - - // then - assertFalse(isAddr); - } - - @Test - void websocket_url_이_정상적으로_구성되는지_확인한다() { - // given - String serverUrl = "localhost:8080"; - String res = String.format("ws://%s/traffic-hunter/tx", serverUrl); - - // when - String websocketUrl = AgentUtil.WEBSOCKET_URL.getUrl(serverUrl); - - // then - assertEquals(res, websocketUrl); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/util/FileUtilsTest.java b/java-agent/src/test/java/ygo/traffichunter/util/FileUtilsTest.java deleted file mode 100644 index 03df9430..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/util/FileUtilsTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package ygo.traffichunter.util; - -import java.io.FileInputStream; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; - -class FileUtilsTest extends AbstractTest { - - @Test - void 파일_경로를_잘_읽어오는지_확인한다() { - - FileInputStream file = FileUtils.getFile("/Users/yungwang-o/Documents/agent-env.yml"); - - Assertions.assertNotNull(file); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/util/UUIDGeneratorTest.java b/java-agent/src/test/java/ygo/traffichunter/util/UUIDGeneratorTest.java deleted file mode 100644 index d33d8682..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/util/UUIDGeneratorTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package ygo.traffichunter.util; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; - -class UUIDGeneratorTest extends AbstractTest { - - @Test - void UUID_KEY_파일을_생성하고_UUID_KEY를_넣는다() { - String generate = UUIDGenerator.generate("test"); - - Assertions.assertNotNull(generate); - System.out.println(generate); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/websocket/MetricWebSocketClientTest.java b/java-agent/src/test/java/ygo/traffichunter/websocket/MetricWebSocketClientTest.java deleted file mode 100644 index ecd93194..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/websocket/MetricWebSocketClientTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package ygo.traffichunter.websocket; - -import java.net.URI; -import java.time.Instant; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.engine.metric.transaction.TransactionInfo; -import ygo.traffichunter.util.AgentUtil; - -class MetricWebSocketClientTest extends AbstractTest { - - @Test - void 웹소켓_통신_할_때_서버가_끊긴경우_예외_발생() { - - MetricWebSocketClient client = new MetricWebSocketClient(URI.create(AgentUtil.WEBSOCKET_URL.getUrl("localhost:9100")), - new AgentMetadata("test", "Test", "test", Instant.now(), new AtomicReference<>(AgentStatus.INITIALIZED))); - - TransactionInfo txInfo = TransactionInfo.create( - "test", - Instant.now(), - Instant.now(), - 153, - "test", - true - ); - - Assertions.assertThrows(RuntimeException.class, () -> client.toSend(txInfo)); - } -} \ No newline at end of file diff --git a/java-agent/src/test/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverterTest.java b/java-agent/src/test/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverterTest.java deleted file mode 100644 index ffeaf70f..00000000 --- a/java-agent/src/test/java/ygo/traffichunter/websocket/converter/SerializationByteArrayConverterTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package ygo.traffichunter.websocket.converter; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.time.Instant; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import ygo.AbstractTest; -import ygo.traffichunter.agent.AgentStatus; -import ygo.traffichunter.agent.engine.metric.metadata.AgentMetadata; -import ygo.traffichunter.agent.engine.metric.metadata.MetadataWrapper; -import ygo.traffichunter.agent.engine.metric.transaction.TransactionInfo; -import ygo.traffichunter.websocket.converter.SerializationByteArrayConverter.MetricType; - -class SerializationByteArrayConverterTest extends AbstractTest { - - private final SerializationByteArrayConverter converter; - private final ObjectMapper mapper; - - public SerializationByteArrayConverterTest() { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - this.mapper = mapper; - this.converter = new SerializationByteArrayConverter(this.mapper); - } - - @Test - void byte_직렬화의_데이터_사이즈를_확인한다() throws JsonProcessingException { - // given - AgentMetadata metadata = new AgentMetadata( - UUID.randomUUID().toString(), - "1.0.0", - "myAgent", - Instant.now(), - new AtomicReference<>(AgentStatus.RUNNING) - ); - - TransactionInfo txInfo = TransactionInfo.create( - "test", - Instant.now(), - Instant.now(), - 153, - "test", - true - ); - - MetadataWrapper metadataWrapper = MetadataWrapper.create(metadata, txInfo); - - // when - String data = mapper.writeValueAsString(metadataWrapper); - byte[] transform = converter.transform(metadataWrapper, MetricType.SYSTEM_METRIC); - - // then - System.out.println(transform.length + " " + data.length()); - } - - @Test - void byte_직렬화를_한_데이터를_역직렬화_한다() { - AgentMetadata metadata = new AgentMetadata( - UUID.randomUUID().toString(), - "1.0.0", - "myAgent", - Instant.now(), - new AtomicReference<>(AgentStatus.RUNNING) - ); - - TransactionInfo txInfo = TransactionInfo.create( - "test", - Instant.now(), - Instant.now(), - 153, - "test", - true - ); - - MetadataWrapper metadataWrapper = MetadataWrapper.create(metadata, txInfo); - - byte[] transform = converter.transform(metadataWrapper, MetricType.TRANSACTION_METRIC); - - // when - MetadataWrapper transactionInfoMetadataWrapper = converter.inverseTransform(transform, new TypeReference<>() {}); - - // then - Assertions.assertEquals(metadata, transactionInfoMetadataWrapper.metadata()); - Assertions.assertEquals(txInfo, transactionInfoMetadataWrapper.data()); - } -} \ No newline at end of file From 76c48a9f8590e8765a16b5feef501eda77e3f768 Mon Sep 17 00:00:00 2001 From: swager253 Date: Mon, 6 Jan 2025 17:47:37 +0900 Subject: [PATCH 06/25] =?UTF-8?q?(#21)=20plugin=20=EA=B3=84=EC=B8=A1=20sdk?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trace/helper/AbstractSpanHelper.java | 5 ++++ java-apm-agent/plugin/plugin-sdk/build.gradle | 1 - .../AbstractTypeMatcherInstrumentation.java | 30 +++++++++++++++++++ .../PluginInstrumentation.java | 5 ++-- .../type/TypeInstrumentation.java | 16 ---------- .../plugin/spring-webmvc/build.gradle | 4 +++ 6 files changed, 41 insertions(+), 20 deletions(-) create mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/helper/AbstractSpanHelper.java create mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java delete mode 100644 java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/helper/AbstractSpanHelper.java b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/helper/AbstractSpanHelper.java new file mode 100644 index 00000000..91ffa161 --- /dev/null +++ b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/helper/AbstractSpanHelper.java @@ -0,0 +1,5 @@ +package org.traffichunter.javaagent.trace.helper; + +public abstract class AbstractSpanHelper { + +} diff --git a/java-apm-agent/plugin/plugin-sdk/build.gradle b/java-apm-agent/plugin/plugin-sdk/build.gradle index a121ab7a..42099469 100644 --- a/java-apm-agent/plugin/plugin-sdk/build.gradle +++ b/java-apm-agent/plugin/plugin-sdk/build.gradle @@ -10,7 +10,6 @@ repositories { } dependencies { - } test { diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java new file mode 100644 index 00000000..f48227f3 --- /dev/null +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java @@ -0,0 +1,30 @@ +package org.traffichunter.javaagent.plugin.sdk.instrumentation; + +import static net.bytebuddy.matcher.ElementMatchers.any; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatcher.Junction; + +public abstract class AbstractTypeMatcherInstrumentation { + + private final String pluginName; + + private final String pluginModuleVersion; + + public AbstractTypeMatcherInstrumentation(final String pluginName, final String pluginModuleVersion) { + this.pluginName = pluginName; + this.pluginModuleVersion = pluginModuleVersion; + } + + protected ElementMatcher.Junction classLoaderMatcher() { return any(); } + + public abstract ElementMatcher typeMatcher(); + + public Junction ignorePackage() { + return null; + } + + protected abstract ElementMatcher isMethod(); +} diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java index e9ef6630..f2186bf7 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java @@ -1,9 +1,8 @@ package org.traffichunter.javaagent.plugin.sdk.instrumentation; import net.bytebuddy.agent.builder.AgentBuilder; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.type.TypeInstrumentation; -public interface PluginInstrumentation extends TypeInstrumentation { +public interface PluginInstrumentation { - void transform(AgentBuilder.Transformer transformer, ClassLoader classLoader); + AgentBuilder.Transformer transform(); } diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java deleted file mode 100644 index 730431dc..00000000 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/type/TypeInstrumentation.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.traffichunter.javaagent.plugin.sdk.instrumentation.type; - -import static net.bytebuddy.matcher.ElementMatchers.any; - -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; - -public interface TypeInstrumentation { - - default ElementMatcher.Junction classLoaderMatcher() { return any(); } - - ElementMatcher typeMatcher(); - - Junction ignorePackage(); -} diff --git a/java-apm-agent/plugin/spring-webmvc/build.gradle b/java-apm-agent/plugin/spring-webmvc/build.gradle index 807f1925..03652c22 100644 --- a/java-apm-agent/plugin/spring-webmvc/build.gradle +++ b/java-apm-agent/plugin/spring-webmvc/build.gradle @@ -11,6 +11,10 @@ repositories { dependencies { implementation project(':java-apm-agent:plugin:plugin-sdk') + implementation project(':java-apm-agent:java-agent-trace') + + implementation 'org.springframework:spring-webmvc:6.2.0' + compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0' } test { From 8fefff9f1f7035a6bcbc309ac42377a645bd65d6 Mon Sep 17 00:00:00 2001 From: swager253 Date: Mon, 6 Jan 2025 17:48:31 +0900 Subject: [PATCH 07/25] =?UTF-8?q?(#21)=20agent=20tracer=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD=20local=20tracer=20->=20global?= =?UTF-8?q?=20tracer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/AgentExecutionEngine.java | 12 ++++-- .../ConfigurableContextInitializer.java | 43 +++---------------- .../javaagent/trace/manager/TraceManager.java | 32 +++++++------- 3 files changed, 32 insertions(+), 55 deletions(-) diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java index 1761c79d..35597d21 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java @@ -39,6 +39,7 @@ import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.trace.exporter.TraceExporter; import org.traffichunter.javaagent.trace.manager.TraceManager; import org.traffichunter.javaagent.trace.queue.TraceQueue; @@ -111,6 +112,7 @@ private void run() { shutdownHook.enableShutdownHook(); } ConfigurableContextInitializer configurableContextInitializer = context.init(); + TraceManager traceManager = configurableContextInitializer.setTraceManager(new TraceExporter()); configurableContextInitializer.retransform(inst); TrafficHunterAgentProperty property = configurableContextInitializer.property(); AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( @@ -125,7 +127,7 @@ private void run() { if(context.isInit()) { log.info("Agent initialization completed."); runnerThread.start(); - registryShutdownHook(context, runner); + registryShutdownHook(context, runner, traceManager); context.close(); } @@ -137,9 +139,13 @@ private void run() { * * @param context The execution context of the agent. * @param runner The agent runner instance responsible for managing execution. + * @param traceManager The trace manager manages a trace's lifecycle. */ - private void registryShutdownHook(final AgentExecutableContext context, final AgentRunner runner) { - shutdownHook.addRuntimeShutdownHook(TraceManager::close); + private void registryShutdownHook(final AgentExecutableContext context, + final AgentRunner runner, + final TraceManager traceManager) { + + shutdownHook.addRuntimeShutdownHook(traceManager::close); shutdownHook.addRuntimeShutdownHook(TraceQueue.INSTANCE::removeAll); shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); shutdownHook.addRuntimeShutdownHook(runner::close); diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java index 197a32a3..3b7a303e 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java @@ -27,15 +27,10 @@ import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import java.io.InputStream; import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; import java.time.Instant; -import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import net.bytebuddy.ByteBuddy; import net.bytebuddy.agent.builder.AgentBuilder.Default; @@ -99,6 +94,10 @@ public TrafficHunterAgentProperty property(final InputStream is) { return env.load(is); } + public TraceManager setTraceManager(final TraceExporter exporter) { + return new TraceManager(exporter); + } + public void retransform(final Instrumentation inst) { new Default() .ignore(ignoreMatchPackage()) @@ -149,41 +148,13 @@ private ElementMatcher getSpringComponentMatcher() { */ public static class TransactionAdvise { - public static final Tracer tracer = TraceManager.configure(new TraceExporter()); - @OnMethodEnter - public static SpanScope enter(@Origin final String method) { - - Span currentSpan = Span.current(); - - Span span = tracer.spanBuilder(method) - .setParent(Context.current().with(currentSpan)) - .setAttribute("method.name", method) - .setStartTimestamp(Instant.now()) - .startSpan(); - - Scope scope = span.makeCurrent(); - - return new SpanScope(span, scope); + public static SpanScope enter(@Origin final Method method) { + return null; } @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { - - if (Objects.nonNull(throwable)) { - - String exception = throwable.getClass().getName() - + " " - + "(" - + throwable.getMessage() - + ")"; - - spanScope.span().recordException(throwable); - spanScope.span().setStatus(StatusCode.ERROR, exception); - } - - spanScope.span().end(Instant.now()); - spanScope.scope().close(); } } } diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java index 3aa95a7b..6cc0b2d9 100644 --- a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java +++ b/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java @@ -23,6 +23,7 @@ */ package org.traffichunter.javaagent.trace.manager; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; @@ -30,9 +31,9 @@ import io.opentelemetry.sdk.trace.IdGenerator; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.util.Objects; +import org.traffichunter.javaagent.trace.exporter.TraceExporter; /** * @author yungwang-o @@ -40,32 +41,31 @@ */ public class TraceManager { - private static final String INSTRUMENTATION_SCOPE_NAME = "instrument-transaction-scope"; + private final OpenTelemetrySdk openTelemetrySdk; - private static volatile SdkTracerProvider provider; + public TraceManager(final TraceExporter exporter) { - private static volatile OpenTelemetrySdk openTelemetrySdk; - - public static Tracer configure(final SpanExporter exporter) { - - provider = SdkTracerProvider.builder() + SdkTracerProvider provider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(exporter)) .setIdGenerator(IdGenerator.random()) .setSampler(Sampler.alwaysOn()) .build(); - openTelemetrySdk = OpenTelemetrySdk.builder() + this.openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(provider) - .build(); - - return openTelemetrySdk.getTracer(INSTRUMENTATION_SCOPE_NAME); + .buildAndRegisterGlobal(); } - public static void close() { - if (Objects.nonNull(provider) && Objects.nonNull(openTelemetrySdk)) { - provider.close(); - openTelemetrySdk.close(); + public static Tracer getTracer(final String instrumentationName) { + if(Objects.isNull(GlobalOpenTelemetry.get())) { + throw new IllegalStateException("OpenTelemetry is not configured. Call configure() first."); } + + return GlobalOpenTelemetry.getTracer(instrumentationName); + } + + public void close() { + openTelemetrySdk.close(); } public record SpanScope(Span span, Scope scope) { } From 6ec3ed109214ad229eefccc3e14a5214166c65ba Mon Sep 17 00:00:00 2001 From: swager253 Date: Mon, 6 Jan 2025 17:48:52 +0900 Subject: [PATCH 08/25] =?UTF-8?q?(#21)=20spring=20webmvc=20plugin=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webmvc/SpringWebMvcInstrumentation.java | 61 +++++++++++++++++++ .../SpringWebMvcPluginInstrumentation.java | 36 ----------- .../SpringWebMvcInstrumentationHelper.java | 41 +++++++++++++ 3 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java delete mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java new file mode 100644 index 00000000..9d760e71 --- /dev/null +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java @@ -0,0 +1,61 @@ +package org.traffichunter.javaagent.plugin.spring.webmvc; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.Argument; +import net.bytebuddy.asm.Advice.Enter; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.asm.Advice.Origin; +import net.bytebuddy.asm.Advice.Thrown; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.spring.webmvc.helper.SpringWebMvcInstrumentationHelper; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +public class SpringWebMvcInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { + + public SpringWebMvcInstrumentation() { + super("spring-webmvc", "spring-webmvc-6.2.0"); + } + + @Override + public Transformer transform() { + return ((builder, typeDescription, classLoader, javaModule, protectionDomain) -> + builder.method(this.isMethod()).intercept(Advice.to(SpringWebMvcDispatcherServletAdvice.class))); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.springframework.web.servlet.DispatcherServlet"); + } + + @Override + protected ElementMatcher isMethod() { + return named("doDispatch"); + } + + public static class SpringWebMvcDispatcherServletAdvice { + + @OnMethodEnter + public static SpanScope enter(@Origin final Method method, + @Argument(0) final HttpServletRequest request, + @Argument(1) final HttpServletResponse response) { + + return SpringWebMvcInstrumentationHelper.start(method, request); + } + + @OnMethodExit(onThrowable = Throwable.class) + public static void end(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + SpringWebMvcInstrumentationHelper.end(spanScope, throwable); + } + } +} diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java deleted file mode 100644 index 37c0c7bd..00000000 --- a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcPluginInstrumentation.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.traffichunter.javaagent.plugin.spring.webmvc; - -import static net.bytebuddy.matcher.ElementMatchers.named; - -import net.bytebuddy.agent.builder.AgentBuilder.Transformer; -import net.bytebuddy.asm.Advice.OnMethodEnter; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; - -public class SpringWebMvcPluginInstrumentation implements PluginInstrumentation { - - @Override - public void transform(final Transformer transformer, final ClassLoader classLoader) { - - } - - @Override - public ElementMatcher typeMatcher() { - return named("org.springframework.web.servlet.DispatcherServlet"); - } - - @Override - public Junction ignorePackage() { - return null; - } - - public static class SpringWebMvcDispatcherServletAdvice { - - @OnMethodEnter - public static void enter() { - - } - } -} diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java new file mode 100644 index 00000000..779af742 --- /dev/null +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java @@ -0,0 +1,41 @@ +package org.traffichunter.javaagent.plugin.spring.webmvc.helper; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Scope; +import jakarta.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +public class SpringWebMvcInstrumentationHelper { + + private static final String SPRING_WEBMVC_INSTRUMENTATION_SCOPE_NAME = "spring-webmvc-tracer"; + + public static SpanScope start(final Method method, final HttpServletRequest request) { + + Span span = TraceManager.getTracer(SPRING_WEBMVC_INSTRUMENTATION_SCOPE_NAME) + .spanBuilder(method.getName()) + .setAttribute("http.method", request.getMethod()) + .setAttribute("http.requestURI", request.getRequestURI()) + .startSpan(); + + request.setAttribute("dispatch.span", span); + + return new SpanScope(span, span.makeCurrent()); + } + + public static void end(final SpanScope spanScope, final Throwable throwable) { + + Span span = spanScope.span(); + Scope scope = spanScope.scope(); + + if (throwable != null) { + span.recordException(throwable); + span.setStatus(StatusCode.ERROR, throwable.getMessage()); + } + + span.end(); + scope.close(); + } +} From cc6bd015d03a0a56397bbd83a1a35f44783702f5 Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 17:18:27 +0900 Subject: [PATCH 09/25] =?UTF-8?q?(#21)=20jdbc=20plugin=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java-apm-agent/plugin/jdbc/build.gradle | 24 +++++++ .../jdbc/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 ++ .../jdbc/ConnectionPluginInstrumentation.java | 58 +++++++++++++++ ...PrepareStatementPluginInstrumentation.java | 59 +++++++++++++++ .../jdbc/StatementPluginInstrumentation.java | 60 ++++++++++++++++ .../helper/JdbcInstrumentationHelper.java | 68 ++++++++++++++++++ settings.gradle | 1 + 8 files changed, 276 insertions(+) create mode 100644 java-apm-agent/plugin/jdbc/build.gradle create mode 100644 java-apm-agent/plugin/jdbc/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/plugin/jdbc/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java diff --git a/java-apm-agent/plugin/jdbc/build.gradle b/java-apm-agent/plugin/jdbc/build.gradle new file mode 100644 index 00000000..1227ff64 --- /dev/null +++ b/java-apm-agent/plugin/jdbc/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +group = 'org.traffichunter.javaagent.plugin' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + + implementation project(':java-apm-agent:plugin:plugin-sdk') + implementation project(':java-apm-agent:java-agent-trace') + + +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/plugin/jdbc/gradle/wrapper/gradle-wrapper.jar b/java-apm-agent/plugin/jdbc/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 + builder.method(this.isMethod()).intercept(Advice.to(ConnectionAdvice.class)); + } + + @Override + public ElementMatcher typeMatcher() { + return named("java.sql.Connection"); + } + + @Override + protected ElementMatcher isMethod() { + return named("prepareStatement"); + } + + @SuppressWarnings("unused") + public static class ConnectionAdvice { + + @OnMethodEnter + public static SpanScope start(@Argument(0) final String sql) { + Context parentContext = Context.current(); + + return JdbcInstrumentationHelper.ConnectionInstrumentation.start(sql, parentContext); + } + + @OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + JdbcInstrumentationHelper.end(spanScope, throwable); + } + } +} diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java new file mode 100644 index 00000000..1c7dde02 --- /dev/null +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java @@ -0,0 +1,59 @@ +package org.traffichunter.javaagent.plugin.jdbc; + +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.Enter; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.asm.Advice.Thrown; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.traffichunter.javaagent.plugin.jdbc.helper.JdbcInstrumentationHelper; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +public class PrepareStatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation + implements PluginInstrumentation { + + public PrepareStatementPluginInstrumentation() { + super("jdbc", PrepareStatementPluginInstrumentation.class.getSimpleName(), ""); + } + + @Override + public Transformer transform() { + return ((builder, typeDescription, classLoader, javaModule, protectionDomain) -> + builder.method(this.isMethod()).intercept(Advice.to(PrepareStatementAdvice.class))); + } + + @Override + public ElementMatcher typeMatcher() { + return named("java.sql.PreparedStatement"); + } + + @Override + protected ElementMatcher isMethod() { + return nameStartsWith("execute"); + } + + @SuppressWarnings("unused") + public static class PrepareStatementAdvice { + + @OnMethodEnter + public static SpanScope enter() { + Context parentContext = Context.current(); + + return JdbcInstrumentationHelper.PreparedStatementInstrumentation.start(parentContext); + } + + @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + JdbcInstrumentationHelper.end(spanScope, throwable); + } + } +} diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java new file mode 100644 index 00000000..258b568a --- /dev/null +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java @@ -0,0 +1,60 @@ +package org.traffichunter.javaagent.plugin.jdbc; + +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.Argument; +import net.bytebuddy.asm.Advice.Enter; +import net.bytebuddy.asm.Advice.OnMethodEnter; +import net.bytebuddy.asm.Advice.OnMethodExit; +import net.bytebuddy.asm.Advice.Thrown; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.traffichunter.javaagent.plugin.jdbc.helper.JdbcInstrumentationHelper; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +public class StatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation + implements PluginInstrumentation { + + public StatementPluginInstrumentation() { + super("jdbc", StatementPluginInstrumentation.class.getSimpleName(),""); + } + + @Override + public Transformer transform() { + return ((builder, typeDescription, classLoader, javaModule, protectionDomain) -> + builder.method(this.isMethod()).intercept(Advice.to(StatementAdvice.class))); + } + + @Override + public ElementMatcher typeMatcher() { + return named("java.sql.Statement"); + } + + @Override + protected ElementMatcher isMethod() { + return nameStartsWith("execute"); + } + + @SuppressWarnings("unused") + public static class StatementAdvice { + + @OnMethodEnter + public static SpanScope enter(@Argument(0) final String sql) { + Context parentContext = Context.current(); + + return JdbcInstrumentationHelper.StatementInstrumentation.start(sql, parentContext); + } + + @OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { + JdbcInstrumentationHelper.end(spanScope, throwable); + } + } +} diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java new file mode 100644 index 00000000..666318cb --- /dev/null +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java @@ -0,0 +1,68 @@ +package org.traffichunter.javaagent.plugin.jdbc.helper; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import org.traffichunter.javaagent.trace.manager.TraceManager; +import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; + +public class JdbcInstrumentationHelper { + + private static final String JDBC_INSTRUMENTATION_SCOPE_NAME = "jdbc-tracer"; + + public static class ConnectionInstrumentation { + + public static SpanScope start(final String sql, final Context parentContext) { + + Span span = TraceManager.getTracer(JDBC_INSTRUMENTATION_SCOPE_NAME) + .spanBuilder("connectionSpan") + .setParent(parentContext) + .setAttribute("sql", sql) + .startSpan(); + + return new SpanScope(span, span.makeCurrent()); + } + } + + public static class StatementInstrumentation { + + public static SpanScope start(final String sql, final Context parentContext) { + + Span span = TraceManager.getTracer(JDBC_INSTRUMENTATION_SCOPE_NAME) + .spanBuilder("statementSpan") + .setParent(parentContext) + .setAttribute("sql", sql) + .startSpan(); + + return new SpanScope(span, span.makeCurrent()); + } + } + + public static class PreparedStatementInstrumentation { + + public static SpanScope start(final Context parentContext) { + + Span span = TraceManager.getTracer(JDBC_INSTRUMENTATION_SCOPE_NAME) + .spanBuilder("preparedStatementSpan") + .setParent(parentContext) + .startSpan(); + + return new SpanScope(span, span.makeCurrent()); + } + } + + public static void end(final SpanScope spanScope, final Throwable throwable) { + + Span span = spanScope.span(); + Scope scope = spanScope.scope(); + + if (throwable != null) { + span.recordException(throwable); + span.setStatus(StatusCode.ERROR, throwable.getMessage()); + } + + span.end(); + scope.close(); + } +} diff --git a/settings.gradle b/settings.gradle index 14c924a0..623b5578 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include(':java-apm-agent') include(':java-apm-agent:plugin') include(':java-apm-agent:plugin:plugin-sdk') include(':java-apm-agent:plugin:spring-webmvc') +include(':java-apm-agent:plugin:jdbc') include(':java-apm-agent:java-agent-trace') include(':java-apm-agent:java-agent-retry') include(':java-apm-agent:java-agent-websocket') From 53d61dc5a45b167c940f134c6d2a675fcd0095a4 Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 17:19:04 +0900 Subject: [PATCH 10/25] =?UTF-8?q?(#21)=20AbstractTypeMatcherInstrumentatio?= =?UTF-8?q?n=20=EC=84=B8=EB=B6=80=EC=A0=81=EC=9D=B8=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EB=84=A4=EC=9E=84=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractTypeMatcherInstrumentation.java | 21 +++++++++++++++++-- .../webmvc/SpringWebMvcInstrumentation.java | 5 +++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java index f48227f3..0e2ad4ad 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java @@ -11,14 +11,19 @@ public abstract class AbstractTypeMatcherInstrumentation { private final String pluginName; + private final String pluginDetailName; + private final String pluginModuleVersion; - public AbstractTypeMatcherInstrumentation(final String pluginName, final String pluginModuleVersion) { + public AbstractTypeMatcherInstrumentation(final String pluginName, + final String pluginDetailName, + final String pluginModuleVersion) { this.pluginName = pluginName; + this.pluginDetailName = pluginDetailName; this.pluginModuleVersion = pluginModuleVersion; } - protected ElementMatcher.Junction classLoaderMatcher() { return any(); } + protected Junction classLoaderMatcher() { return any(); } public abstract ElementMatcher typeMatcher(); @@ -27,4 +32,16 @@ public Junction ignorePackage() { } protected abstract ElementMatcher isMethod(); + + public String getPluginName() { + return pluginName; + } + + public String getPluginDetailName() { + return pluginDetailName; + } + + public String getPluginModuleVersion() { + return pluginModuleVersion; + } } diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java index 9d760e71..498d867e 100644 --- a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java @@ -24,7 +24,7 @@ public class SpringWebMvcInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { public SpringWebMvcInstrumentation() { - super("spring-webmvc", "spring-webmvc-6.2.0"); + super("spring-webmvc", SpringWebMvcInstrumentation.class.getSimpleName(),"spring-webmvc-6.2.0"); } @Override @@ -43,6 +43,7 @@ protected ElementMatcher isMethod() { return named("doDispatch"); } + @SuppressWarnings("unused") public static class SpringWebMvcDispatcherServletAdvice { @OnMethodEnter @@ -53,7 +54,7 @@ public static SpanScope enter(@Origin final Method method, return SpringWebMvcInstrumentationHelper.start(method, request); } - @OnMethodExit(onThrowable = Throwable.class) + @OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void end(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { SpringWebMvcInstrumentationHelper.end(spanScope, throwable); } From fc2e1b558f68ac7bdae8320c79a9a2a506930546 Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 17:26:05 +0900 Subject: [PATCH 11/25] =?UTF-8?q?[no=20issue]=20readme=20=EC=99=80=20class?= =?UTF-8?q?=20=EB=9D=BC=EC=9D=B4=EC=84=BC=EC=8A=A4=20=EB=B0=8F=20=EA=B8=B0?= =?UTF-8?q?=EC=97=AC=EC=9E=90=20=EA=B8=B0=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../jdbc/ConnectionPluginInstrumentation.java | 4 +++ ...PrepareStatementPluginInstrumentation.java | 27 ++++++++++++++++++ .../jdbc/StatementPluginInstrumentation.java | 27 ++++++++++++++++++ .../helper/JdbcInstrumentationHelper.java | 27 ++++++++++++++++++ .../plugin/sdk/constant/PluginConstant.java | 27 ++++++++++++++++++ .../AbstractTypeMatcherInstrumentation.java | 27 ++++++++++++++++++ .../PluginInstrumentation.java | 27 ++++++++++++++++++ .../plugin/sdk/loader/PluginLoader.java | 28 +++++++++++++++++++ .../sdk/loader/TrafficHunterPluginLoader.java | 27 ++++++++++++++++++ .../webmvc/SpringWebMvcInstrumentation.java | 27 ++++++++++++++++++ .../SpringWebMvcInstrumentationHelper.java | 27 ++++++++++++++++++ 12 files changed, 276 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ca93e6c..53e014c0 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The pre-release version is v1.0.0. ```text The MIT License -Copyright (c) 2024 yungwang-o +Copyright (c) 2024 traffic-hunter.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java index bef91b3c..4c5ab061 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java @@ -18,6 +18,10 @@ import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class ConnectionPluginInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { public ConnectionPluginInstrumentation() { diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java index 1c7dde02..75f9076f 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.jdbc; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; @@ -18,6 +41,10 @@ import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class PrepareStatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java index 258b568a..ac5829d0 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.jdbc; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; @@ -19,6 +42,10 @@ import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class StatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java index 666318cb..30f68b4d 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.jdbc.helper; import io.opentelemetry.api.trace.Span; @@ -7,6 +30,10 @@ import org.traffichunter.javaagent.trace.manager.TraceManager; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class JdbcInstrumentationHelper { private static final String JDBC_INSTRUMENTATION_SCOPE_NAME = "jdbc-tracer"; diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java index e88902ff..4d351372 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java @@ -1,5 +1,32 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.sdk.constant; +/** + * @author yungwang-o + * @version 1.1.0 + */ public enum PluginConstant { SPRING_BOOT, diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java index 0e2ad4ad..be539e56 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.sdk.instrumentation; import static net.bytebuddy.matcher.ElementMatchers.any; @@ -7,6 +30,10 @@ import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher.Junction; +/** + * @author yungwang-o + * @version 1.1.0 + */ public abstract class AbstractTypeMatcherInstrumentation { private final String pluginName; diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java index f2186bf7..73655bc1 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java @@ -1,7 +1,34 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.sdk.instrumentation; import net.bytebuddy.agent.builder.AgentBuilder; +/** + * @author yungwang-o + * @version 1.1.0 + */ public interface PluginInstrumentation { AgentBuilder.Transformer transform(); diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java index 1ff60c68..f106fdc6 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/PluginLoader.java @@ -1,7 +1,35 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.sdk.loader; import java.util.List; +/** + * @author yungwang-o + * @version 1.1.0 + * @param

plugin module. + */ public interface PluginLoader

{ List

loadModules(ClassLoader classLoader); diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java index fa750825..1688befc 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.sdk.loader; import java.util.ArrayList; @@ -5,6 +28,10 @@ import java.util.ServiceLoader; import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class TrafficHunterPluginLoader implements PluginLoader { @Override diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java index 498d867e..518b9a62 100644 --- a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.spring.webmvc; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -21,6 +44,10 @@ import org.traffichunter.javaagent.plugin.spring.webmvc.helper.SpringWebMvcInstrumentationHelper; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class SpringWebMvcInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { public SpringWebMvcInstrumentation() { diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java index 779af742..a1408658 100644 --- a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.spring.webmvc.helper; import io.opentelemetry.api.trace.Span; @@ -8,6 +31,10 @@ import org.traffichunter.javaagent.trace.manager.TraceManager; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; +/** + * @author yungwang-o + * @version 1.1.0 + */ public class SpringWebMvcInstrumentationHelper { private static final String SPRING_WEBMVC_INSTRUMENTATION_SCOPE_NAME = "spring-webmvc-tracer"; From 17c330ff7dd7f6a8cce782fa421d9041f5e3d666 Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 20:38:08 +0900 Subject: [PATCH 12/25] =?UTF-8?q?(#21)=20plugin=20abstract=20class=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java-agent-bootstrap/build.gradle | 2 +- .../ConfigurableContextInitializer.java | 81 ++++++------------- .../jdbc/ConnectionPluginInstrumentation.java | 5 +- ...PrepareStatementPluginInstrumentation.java | 6 +- .../jdbc/StatementPluginInstrumentation.java | 6 +- ...ava => AbstractPluginInstrumentation.java} | 13 +-- .../PluginInstrumentation.java | 4 - .../sdk/loader/TrafficHunterPluginLoader.java | 13 +-- .../webmvc/SpringWebMvcInstrumentation.java | 5 +- 9 files changed, 48 insertions(+), 87 deletions(-) rename java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/{AbstractTypeMatcherInstrumentation.java => AbstractPluginInstrumentation.java} (86%) diff --git a/java-apm-agent/java-agent-bootstrap/build.gradle b/java-apm-agent/java-agent-bootstrap/build.gradle index 82872ea4..61a1f8b3 100644 --- a/java-apm-agent/java-agent-bootstrap/build.gradle +++ b/java-apm-agent/java-agent-bootstrap/build.gradle @@ -36,7 +36,7 @@ dependencies { implementation project(':java-apm-agent:java-agent-trace') implementation project(':java-apm-agent:java-agent-retry') implementation project(':java-apm-agent:java-agent-jmx') - implementation project(':java-apm-agent:plugin') + implementation project(':java-apm-agent:plugin:plugin-sdk') implementation 'io.opentelemetry:opentelemetry-api:1.45.0' implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java index 3b7a303e..b7d4cbc8 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java @@ -23,39 +23,27 @@ */ package org.traffichunter.javaagent.bootstrap.engine.context.configuration; -import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.named; - import java.io.InputStream; import java.lang.instrument.Instrumentation; -import java.lang.reflect.Method; import java.time.Instant; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder.Default; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.asm.Advice.Enter; -import net.bytebuddy.asm.Advice.OnMethodEnter; -import net.bytebuddy.asm.Advice.OnMethodExit; -import net.bytebuddy.asm.Advice.Origin; -import net.bytebuddy.asm.Advice.Thrown; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatcher.Junction; -import net.bytebuddy.matcher.ElementMatchers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; import org.traffichunter.javaagent.bootstrap.engine.env.Environment; -import org.traffichunter.javaagent.bootstrap.engine.instrument.annotation.AnnotationPath; import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.commons.util.UUIDGenerator; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.loader.PluginLoader; +import org.traffichunter.javaagent.plugin.sdk.loader.TrafficHunterPluginLoader; import org.traffichunter.javaagent.trace.exporter.TraceExporter; import org.traffichunter.javaagent.trace.manager.TraceManager; -import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; /** * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, @@ -98,13 +86,23 @@ public TraceManager setTraceManager(final TraceExporter exporter) { return new TraceManager(exporter); } + /** + * Load all plugins to manipulate the target application's bytecode. + */ public void retransform(final Instrumentation inst) { - new Default() - .ignore(ignoreMatchPackage()) - .type(getSpringComponentMatcher()) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(TransactionAdvise.class).on(isMethod())) - ).installOn(inst); + + List plugins = + loadPlugins(ConfigurableContextInitializer.class.getClassLoader()); + + AgentBuilder.Default agentBuilder = new AgentBuilder.Default(); + + for(AbstractPluginInstrumentation plugin : plugins) { + agentBuilder = (Default) agentBuilder + .type(plugin.typeMatcher()) + .transform(plugin.transform()); + } + + agentBuilder.installOn(inst); } public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { @@ -120,41 +118,10 @@ public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus ); } - private Junction ignoreMatchPackage() { - return ElementMatchers.nameStartsWith("java.") - .or(ElementMatchers.nameStartsWith("sun.")) - .or(ElementMatchers.nameStartsWith("jdk.")); - } - - private ElementMatcher getSpringComponentMatcher() { - return isAnnotatedWith(named(AnnotationPath.SERVICE.getPath())) - .or(isAnnotatedWith(named(AnnotationPath.REST_CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.CONTROLLER.getPath()))) - .or(isAnnotatedWith(named(AnnotationPath.REPOSITORY.getPath()))); - } - - /** - *

- * Intercepts method execution to create a span for tracing. - * Handles span lifecycle, recording exceptions, and closing the scope. - *

- * - *

Note: Ensure the class has a public access modifier to avoid - * visibility issues when used with external components or frameworks like ByteBuddy. - * Using a private or package-private access modifier may result in runtime errors - * due to restricted access.

- * - * @see TraceManager - */ - public static class TransactionAdvise { + private List loadPlugins(final ClassLoader classLoader) { - @OnMethodEnter - public static SpanScope enter(@Origin final Method method) { - return null; - } + PluginLoader pluginLoader = new TrafficHunterPluginLoader(); - @OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void exit(@Enter final SpanScope spanScope, @Thrown final Throwable throwable) { - } + return pluginLoader.loadModules(classLoader); } } diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java index 4c5ab061..b18dde98 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java @@ -14,15 +14,14 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.traffichunter.javaagent.plugin.jdbc.helper.JdbcInstrumentationHelper; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; /** * @author yungwang-o * @version 1.1.0 */ -public class ConnectionPluginInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { +public class ConnectionPluginInstrumentation extends AbstractPluginInstrumentation { public ConnectionPluginInstrumentation() { super("jdbc", ConnectionPluginInstrumentation.class.getSimpleName(), ""); diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java index 75f9076f..45872e21 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/PrepareStatementPluginInstrumentation.java @@ -37,16 +37,14 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.traffichunter.javaagent.plugin.jdbc.helper.JdbcInstrumentationHelper; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; /** * @author yungwang-o * @version 1.1.0 */ -public class PrepareStatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation - implements PluginInstrumentation { +public class PrepareStatementPluginInstrumentation extends AbstractPluginInstrumentation { public PrepareStatementPluginInstrumentation() { super("jdbc", PrepareStatementPluginInstrumentation.class.getSimpleName(), ""); diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java index ac5829d0..ea8c2fd6 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/StatementPluginInstrumentation.java @@ -38,16 +38,14 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.traffichunter.javaagent.plugin.jdbc.helper.JdbcInstrumentationHelper; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; /** * @author yungwang-o * @version 1.1.0 */ -public class StatementPluginInstrumentation extends AbstractTypeMatcherInstrumentation - implements PluginInstrumentation { +public class StatementPluginInstrumentation extends AbstractPluginInstrumentation { public StatementPluginInstrumentation() { super("jdbc", StatementPluginInstrumentation.class.getSimpleName(),""); diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java similarity index 86% rename from java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java rename to java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java index be539e56..a551ade0 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractTypeMatcherInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java @@ -25,6 +25,7 @@ import static net.bytebuddy.matcher.ElementMatchers.any; +import net.bytebuddy.agent.builder.AgentBuilder.Transformer; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -34,7 +35,7 @@ * @author yungwang-o * @version 1.1.0 */ -public abstract class AbstractTypeMatcherInstrumentation { +public abstract class AbstractPluginInstrumentation { private final String pluginName; @@ -42,18 +43,20 @@ public abstract class AbstractTypeMatcherInstrumentation { private final String pluginModuleVersion; - public AbstractTypeMatcherInstrumentation(final String pluginName, - final String pluginDetailName, - final String pluginModuleVersion) { + public AbstractPluginInstrumentation(final String pluginName, + final String pluginDetailName, + final String pluginModuleVersion) { this.pluginName = pluginName; this.pluginDetailName = pluginDetailName; this.pluginModuleVersion = pluginModuleVersion; } - protected Junction classLoaderMatcher() { return any(); } + public abstract Transformer transform(); public abstract ElementMatcher typeMatcher(); + protected Junction classLoaderMatcher() { return any(); } + public Junction ignorePackage() { return null; } diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java index 73655bc1..450b0126 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java @@ -23,13 +23,9 @@ */ package org.traffichunter.javaagent.plugin.sdk.instrumentation; -import net.bytebuddy.agent.builder.AgentBuilder; - /** * @author yungwang-o * @version 1.1.0 */ public interface PluginInstrumentation { - - AgentBuilder.Transformer transform(); } diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java index 1688befc..75699356 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java +++ b/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/loader/TrafficHunterPluginLoader.java @@ -26,22 +26,23 @@ import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; /** * @author yungwang-o * @version 1.1.0 */ -public class TrafficHunterPluginLoader implements PluginLoader { +public class TrafficHunterPluginLoader implements PluginLoader { @Override - public List loadModules(final ClassLoader classLoader) { + public List loadModules(final ClassLoader classLoader) { - List loadPlugIn = new ArrayList<>(); + List loadPlugIn = new ArrayList<>(); - ServiceLoader loader = ServiceLoader.load(PluginInstrumentation.class, classLoader); + ServiceLoader loader = + ServiceLoader.load(AbstractPluginInstrumentation.class, classLoader); - for(PluginInstrumentation plugIn : loader) { + for(AbstractPluginInstrumentation plugIn : loader) { loadPlugIn.add(plugIn); } diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java index 518b9a62..47132589 100644 --- a/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java +++ b/java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentation.java @@ -39,8 +39,7 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractTypeMatcherInstrumentation; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.PluginInstrumentation; +import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; import org.traffichunter.javaagent.plugin.spring.webmvc.helper.SpringWebMvcInstrumentationHelper; import org.traffichunter.javaagent.trace.manager.TraceManager.SpanScope; @@ -48,7 +47,7 @@ * @author yungwang-o * @version 1.1.0 */ -public class SpringWebMvcInstrumentation extends AbstractTypeMatcherInstrumentation implements PluginInstrumentation { +public class SpringWebMvcInstrumentation extends AbstractPluginInstrumentation { public SpringWebMvcInstrumentation() { super("spring-webmvc", SpringWebMvcInstrumentation.class.getSimpleName(),"spring-webmvc-6.2.0"); From b9f6d8f2f817548812facfa95377315c20610c71 Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 22:13:11 +0900 Subject: [PATCH 13/25] =?UTF-8?q?[no=20issue]=20licence=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jdbc/ConnectionPluginInstrumentation.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java index b18dde98..e686209a 100644 --- a/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java +++ b/java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/ConnectionPluginInstrumentation.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.traffichunter.javaagent.plugin.jdbc; import static net.bytebuddy.matcher.ElementMatchers.named; From 1e448a8e1728e9c9e70ba469d5b45013a834e26b Mon Sep 17 00:00:00 2001 From: swager253 Date: Tue, 7 Jan 2025 22:13:54 +0900 Subject: [PATCH 14/25] =?UTF-8?q?(#21)=20spring,=20jdbc=20SPI(Service=20Pr?= =?UTF-8?q?ovider=20Interface)=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nt.plugin.sdk.instrumentation.AbstractPluginInstrumentation | 3 +++ ...nt.plugin.sdk.instrumentation.AbstractPluginInstrumentation | 1 + 2 files changed, 4 insertions(+) create mode 100644 java-apm-agent/plugin/jdbc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation diff --git a/java-apm-agent/plugin/jdbc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation b/java-apm-agent/plugin/jdbc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation new file mode 100644 index 00000000..47185ca9 --- /dev/null +++ b/java-apm-agent/plugin/jdbc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation @@ -0,0 +1,3 @@ +org.traffichunter.javaagent.plugin.jdbc.StatementPluginInstrumentation +org.traffichunter.javaagent.plugin.jdbc.PrepareStatementPluginInstrumentation +org.traffichunter.javaagent.plugin.jdbc.ConnectionPluginInstrumentation \ No newline at end of file diff --git a/java-apm-agent/plugin/spring-webmvc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation b/java-apm-agent/plugin/spring-webmvc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation new file mode 100644 index 00000000..33d49a7d --- /dev/null +++ b/java-apm-agent/plugin/spring-webmvc/src/main/resources/META-INF/services/org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation @@ -0,0 +1 @@ +org.traffichunter.javaagent.plugin.spring.webmvc.SpringWebMvcInstrumentation \ No newline at end of file From 4ef1169b8bef35e996b2d99513bbdf4d79f1a2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=EA=B4=91=EC=98=A4?= <37898720+yungwangoh@users.noreply.github.com> Date: Sun, 20 Apr 2025 16:58:48 +0900 Subject: [PATCH 15/25] Release v1.1.0 (#37) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (#23) fix * [no issue] schema 호환성 문제 해결 * [no issue] agent close() 과도한 동시성 로직이 들어감. 이에 대해 refactoring 진행 * [no issue] trace setting 와 plugin load log 기록 순서 변경 * (#21) spring business 모듈 추가 * (#21) spring business 모듈 추가 * (#21) hibernate 계측 plugin 추가 * (#21) plugin-sdk 에 정적 필드를 추가 하여 객체를 공유 하는 기능 추가 * (#21) hibernate gradle include 추가 * (#21) [fix] jdbc 잘못된 계측 수정 인터페이스 계측이 아닌 구현체를 계측 해야됨. * (#21) [refactor] slf4j -> 표준 logger로 변경 * (#21) class loading 문제 미해결로 인한 롤백 * (#21) class loading 문제 미해결로 인한 롤백 * (#21) jdbc 계측 플러그인 support 모듈 구현 * (#26) Trace DTO 스펙 변경 * (#26) Assembler를 통한 Span 노드 재구성 * Feat/broadcast (#25) * feat : broadcast 기능 구현 * refactor : broadcast 기능 개선 * refactor : broadcast 기능 개선 * [no issue] Identification 식별 추상 클래스 추가 * [no issue] agent retry 외부 라이브러리 의존성 격리 * [no issue] span logic no-op으로 null 처리 로직 숨김 * [no issue] FieldMap 동시성 이슈에 대한 lock 작업 * [no issue] exporter 설정 * [no issue] RowMapSupportTest RowMapSupport 역직렬화 테스트 * [no issue] RowMapSupport final 누락 체크 * Refactor/broadcast (#27) * [no issue] refactor : broadcast 기능 개선 및 변경 * [no issue] refactor : 트랜잭션 attributes 예시 추가 * [no issue] refactor : RowMapSupport 객체를 통해 역직렬화 코드로 개선 * [no issue] #27 누락된 부분 및 가독성 재조정 * [no issue] rowMapSupport 예외 처리 변경 * (#28) 통계 서비스 추가 * [no issue] fix : RestDocs payload error 해결 (#29) * [no issue] git ignore update * [no issue] update rest docs * (#28) refactor: pageable parameter 변경 * (#28) test: MetricController 테스트 * (#28) feat: query 보조 유틸 클래스 생성 * (#28) feat: query 보조 유틸 클래스 생성 * (#28) feat: jooq 적용 * (#28) refactor: jooq 환경 변수 주입 * (#28) fix: maxHeapMemoryUsage 변수 double -> int 변경 * [no-issue] refactor: 숫자 가독성 높임 * [no-issue] response status 추가 * [no-issue] ErrorResponse static factory method 로 변경 * (#28) test: rest docs 작성 * [no-issue] docs: statistics api docs 추가 및 docs title 변경 * [no-issue] refactor: channel exception or method abstraction * [no-issue] refactor: byte array decompressor exception * [no-issue] refactor: mvcMvcRequestBuilders -> restDocsRequestBuilders * [no-issue] feat: realtime repository * [no-issue] comment: MetricCollector 변경 * (#30) feat: Sender interface * (#30) feat: discord web hook alarm * [no-issue] refactor: time interval limit * (#30) feat: slack webhook * (#30) feat: slack webhook * [no-issue] feat: view sender * [no-issue] comment: licence * (#30) refactor: slack web-hook pretty message print * [no-issue] feat: service transaction cacheable * (#30) feat: server sent event Alarm, View sender 추가 * [no-issue] refactor: final parameter * (#28) refactor: service transaction spec 변경 * Refactor/broadcast (#31) * [no issue] refactor : api 문서 변경 * [no issue] refactor : boradcast validation 추가 및 schedule default 값 설정 * [no issue] refactor : broadcast schedule interval 입력 받는게 아니라 default로 변경 * [no issue] refactor : broadcast 요구사항 변경에 의한 쿼리 변경 및 응답값 변경 * [no-issue] feat: service transaction cacheable * (#30) feat: server sent event Alarm, View sender 추가 * [no-issue] refactor: final parameter * (#28) refactor: service transaction spec 변경 * [no issue] refactor : api 문서 변경 * [no issue] refactor : boradcast validation 추가 및 schedule default 값 설정 * [no issue] conflict * [no issue] refactor : broadcast 요구사항 변경에 의한 쿼리 변경 및 응답값 변경 * [no issue] refactor : MetricData domain객체를 API응답 값으로 쓰던 것을 MetricDataResponse 생성하여 변경 * [no issue] conflict * [no issue] conflict --------- Co-authored-by: swager253 * [no-issue] fix: interval * [no-issue] rename: service transaction method * [no-issue] refactor: agent trace info spec change * [no-issue] refactor: normal lock add * (#30) feat: add alarm control interface method * (#28) feat: add statistics service transaction * (#28) test: statistics service transaction * (#30) feat: alarm enable or disable * (#30) feat: alarm global exception handler * (#30) refactor: alarm message inet deprecated * (#30) test: message maker inet deprecated * (#30) fix: bad grammar update query * [no-issue] feat: add member service * [no-issue] move: webhook, websocket package move * [no-issue] refactor: member id is not null * [no-issue] test: member restdocs impl * [no-issue] rename: MetricControllerAdvice.java -> GlobalControllerAdvice * (#30) feat: Threshold impl * [no-issue] fix: Unterminated dollar quote started at position -> resolve: double quote * [no-issue] refactor: threshold architecture refactoring * [no-issue] refactor: transaction readOnly * [no-issue] feat: alarm rest docs impl * Feat/alram (#32) * (#30) alarm rebase merge * (#30) feat : 임계치에 따른 알람 기능 구현 * (#30) refactor: integrated webhook or sse * [no-issue] feat: add ServerSentEventException global exception handler * (#30) refactor : ViewSender 인터페이스 추가 및 캐시 설정 (#33) * [no-issue] refactor: code reformatting * [no-issue] refactor: code reformatting * [no-issue] move: move message from web hook package to alarm package * [no-issue] refactor: sendAll, asyncSend * [no-issue] refactor: fix sse name * [no-issue] refactor: add comment rest docs instant time UTC * [no-issue] refactor: handle exception return null * [no-issue] refactor: update threshold cache evict * (#30) feat: alarm webhook api * (#30) test: alarm webhook api * (#30) docs: write webhook restdocs * [no-issue] update license comment * (#30) feat: add sse alarm message timestamp * [no-issue] refactor: api guide * (#30) refactor: add final * (#30) feat: alarm loss prevention and dead letter * [no-issue] test: check failed test * [no-issue] docs: update statistics rest docs * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] docs: publish API docs to GitHub Pages * [no-issue] refactor: delete webhook url * (#30) refactor: optimize bulkSoftDeleteDeadLetter * (#30) refactor: change return object DeadLetterResponse -> DeadLetter * (#30) refactor: Message class to record * (#30) test: dead letter schedule * (#30) remove * [no-issue] refactor: application.yml set webhook url default value * [no-issue] test: MetricStatisticsServiceTest disabled service transaction detail * [no-issue] agent rebuilding v1.1.0 * [no-issue] agent rebuilding v1.1.0 * [no-issue] fix byte buddy ref error * [no-issue] feat: java-agent http-url-connection plugin * [no-issue] feat: register service http-url-connection * [no-issue] feat: http-client plugin * [no-issue] feat: call depth and thread local * [no-issue] refactor: Instrumentor end builder * [no-issue] feat: http-client plugin * [no-issue] remove: hikariCP plugin * [no-issue] comment: classLoaderMatcher no override * [no-issue] refactor: add slf4j * [no-issue] feat: servlet plugin * [no-issue] feat: servlet plugin * [no-issue] feat: span name method * [no-issue] refactor: add call depth * [no-issue] feat: servlet plugin * [no-issue] feat: servlet plugin * [no-issue] test: SpanScope Noop equal * [no-issue] refactor: delete abstract method * [no-issue] feat: servlet plugin * [no-issue] add restClient ex) external api call test * [no-issue] gradle dependencies clear * [no-issue] feat: http client helper classes * [no-issue] feat: add instrumentationName * [no-issue] feat: add instrumentationName * [no-issue] move package * [no-issue] rename class * [no-issue] resetting spi * [no-issue] feat: instrumentationName * [no-issue] move package extension/bootstrap -> extension/ * [no-issue] fix: reflection access public * [no-issue] rename: change method name or comment * [no-issue] feat: add CallDepth getDepth * [no-issue] test: callDepth * [no-issue] refactor: It didn't work as expected, so I removed CallDepth. * [no-issue] refactor: remove import ConfigurableContextInitializer * [no-issue] feat: Add Configurations * [no-issue] feat: Added to extend the matcher's scope to bootstrap. * [no-issue] feat: Redefine location strategy platform, bootstrap * [no-issue] comment: Update comment * [no-issue] feat: Add exporter logging * [no-issue] fix: Add ForAdvice include() extend boot, platform, app area * [no-issue] move: StartUp class module bootstrap to extension * [no-issue] refactor: Add banner mode * [no-issue] test: banner mode * [no-issue] refactor: Change spec JMX collector interface * [no-issue] fix: javax servlet -> jakarta servlet * [no-issue] fix: resolve caffeine cache issue Visit https://github.com/ben-manes/caffeine/wiki/Cleanup * [no-issue] refactor: inject context * [no-issue] refactor: inject context * [no-issue] refactor: ScheduleExecutorService -> TaskScheduler * [no-issue] refactor: memory visibility * [no-issue] refactor: ScheduleExecutorService -> TaskScheduler * [no-issue] test: binary compress * feat : (#34) /member check 기능 구현 및 주요 기능 테스트 보강 (#35) * (#34) feat : Member Session check 기능 - Member가 Session에 등록되어 있는지, 아직 유효한지에 대한 체크기능 추가 - LoginInterceptor를 통해 Session 확인하여 유효한지 체크 * [no-issue] refactor : extend supported message types and add logging * [no-issue] feat : implement member deletion functionality * [no-issue] test : member check restdocs add * [no-issue] refactor : register clientMap as a bean * [no-issue] refactor : modify SseMessage to contain Message * [no-issue] test : verify if the calculation works as expected threshold * [no-issue] test : verify if deadletter is created when client sends an error * [no-issue] refactor : update to set default values when no input is provided * [no-issue] test : verify if the Ant pattern can filter paths correctly --------- Co-authored-by: 윤광오 <37898720+yungwangoh@users.noreply.github.com> * [no-issue] feat: add spring auto configure * [no-issue] feat: add spring auto configure * [no-issue] feat: add logger instrumentation plugin * [no-issue] feat: add zipkin span exporter * [no-issue] feat: add logger instrumentation plugin * [no-issue] feat: jul shaded logger plugin * [no-issue] feat: add logback instrumentation plugin * [no-issue] feat: add logback instrumentation plugin * [no-issue] feat: add logback instrumentation plugin * [no-issue] feat: add observability log * [no-issue] feat: add LogRecord body * [no-issue] deprecated TraceQueue * [no-issue] close alarm event * [no-issue] docs: Traffic hunter v1.1.0 readme * [no-issue] docs: Traffic hunter v1.1.0 readme * [no-issue] docs: Traffic hunter v1.1.0 readme * [no-issue] docs: Traffic hunter v1.1.0 readme * [no-issue] docs: Traffic hunter v1.1.0 readme * [no-issue] deprecated LogQueue, MetricSender * [no-issue] no usage dead letter * [no-issue] git action v1.1.0 release --------- Co-authored-by: JuSeong1130 <53209324+JuSeong1130@users.noreply.github.com> --- .github/release-drafter-config.yml | 26 + .github/workflows/release.yml | 13 +- .gitignore | 1 + README.md | 45 +- doc/image/log.png | Bin 0 -> 286582 bytes doc/image/trace.jpeg | Bin 0 -> 145172 bytes java-apm-agent/build.gradle | 17 - java-apm-agent/build.gradle.kts | 21 + .../java-agent-bootstrap/build.gradle | 57 - .../java-agent-bootstrap/build.gradle.kts | 21 + .../javaagent/TrafficHunterAgentMain.java | 119 + .../bootstrap/AgentExecutionEngine.java | 104 + .../instrument/locator => }/AgentLocator.java | 6 +- .../instrument/bootstrap => }/BootState.java | 2 +- .../javaagent/bootstrap/BootstrapLogger.java | 178 + ...BootstrapMain.java => Configurations.java} | 52 +- .../bootstrap/InstrumentationHolder.java | 47 + .../{engine/lifecycle => }/LifeCycle.java | 2 +- .../bootstrap/OpenTelemetrySdkBridge.java | 51 + .../TrafficHunterAgentClassLoader.java | 356 ++ .../TrafficHunterAgentShutdownHook.java | 9 +- .../bootstrap/TrafficHunterAgentStarter.java | 37 + .../engine/AgentExecutionEngine.java | 238 -- .../ConfigurableContextInitializer.java | 127 - .../instrument/annotation/AnnotationPath.java | 57 - .../manager/MetricSendSessionManager.java | 198 -- .../websocket/AgentSystemMetricSender.java | 77 - .../AgentTransactionMetricSender.java | 87 - .../bootstrap/BootstrapLoggerTest.java | 17 + .../bootstrap/ConfigurationsTest.java | 17 + .../java-agent-commons/build.gradle | 19 - .../java-agent-commons/build.gradle.kts | 19 + .../javaagent/commons/type/MetricType.java | 18 + java-apm-agent/java-agent-event/build.gradle | 21 - .../java-agent-event/build.gradle.kts | 21 + .../java-agent-extension/build.gradle.kts | 34 + .../AbstractPluginInstrumentation.java | 44 +- .../extension}/AgentExecutableContext.java | 5 +- .../ConfigurableContextInitializer.java | 174 + .../javaagent/extension/LogRecord.java | 159 + .../extension/OpenTelemetryManager.java | 102 + .../extension/OpenTelemetryParser.java | 47 + .../extension/PluginToolClassLoader.java | 94 + .../javaagent/extension}/TraceInfo.java | 12 +- .../javaagent/extension}/TraceQueue.java | 4 +- .../TrafficHunterAgentExecutableContext.java | 53 +- .../TrafficHunterAgentStartAction.java | 293 ++ .../javaagent/extension/Transformer.java | 59 + .../javaagent/extension/Utilizr.java | 84 + .../extension}/banner/AsciiBanner.java | 13 +- .../bytebuddy/AdjustTransformer.java | 47 + .../bytebuddy/AgentIgnoreMatcher.java | 45 + .../bytebuddy/AgentLocationStrategy.java | 52 + .../extension/bytebuddy/ByteBuddyLogger.java | 105 + .../env/ConfigurableEnvironment.java | 6 +- .../javaagent/extension}/env/Environment.java | 2 +- .../env/yaml/YamlConfigurableEnvironment.java | 14 +- .../env/yaml/bind/RelaxedBindingUtils.java | 6 +- .../env/yaml/root/RootYamlProperty.java | 4 +- .../env/yaml/root/agent/AgentSubProperty.java | 4 +- .../root/agent/retry/RetrySubProperty.java | 4 +- .../retry/backoff/BackOffSubProperty.java | 2 +- .../thunter/TrafficHunterLogExporter.java | 118 + .../thunter/TrafficHunterSpanExporter.java | 101 + .../zipkin/ZipkinSpanExportDelegator.java} | 58 +- .../extension}/loader/PluginLoader.java | 2 +- .../loader/TrafficHunterPluginLoader.java | 4 +- .../extension}/metadata/AgentMetadata.java | 2 +- .../extension}/metadata/MetadataWrapper.java | 2 +- .../property/TrafficHunterAgent.java | 7 +- .../property/TrafficHunterAgentProperty.java | 2 +- .../FaultTolerantTrafficHunterAgent.java | 8 +- .../src/main/resources/agent-banner.txt | 2 +- java-apm-agent/java-agent-jmx/build.gradle | 19 - .../java-agent-jmx/build.gradle.kts | 21 + ...llectSupport.java => JmxMetricSender.java} | 36 +- .../collect/AbstractMBeanMetricCollector.java | 14 +- .../systeminfo/cpu/CpuMetricCollector.java | 22 - .../gc/GarbageCollectionMetricCollector.java | 20 - .../memory/MemoryMetricCollector.java | 34 - .../runtime/RuntimeMetricCollector.java | 23 - .../thread/ThreadMetricCollector.java | 22 - java-apm-agent/java-agent-retry/build.gradle | 21 - .../java-agent-retry/build.gradle.kts | 21 + .../javaagent/retry/RetryHelper.java | 12 - .../retry/backoff/BackOffPolicy.java | 3 + .../retry/executor/RetryExecutor.java | 70 + java-apm-agent/java-agent-trace/build.gradle | 24 - .../trace/helper/AbstractSpanHelper.java | 5 - .../javaagent/trace/manager/TraceManager.java | 72 - .../java-agent-websocket/build.gradle | 26 - .../java-agent-websocket/build.gradle.kts | 27 + .../websocket/MetricWebSocketClient.java | 41 +- .../TrafficHunterWebsocketClient.java | 162 + .../SerializationByteArrayConverter.java | 15 +- .../property/WebSocketRetryProperty.java | 33 + .../SerializationByteArrayConverterTest.java | 49 + java-apm-agent/java-agent/build.gradle.kts | 143 + java-apm-agent/plugin-sdk/build.gradle.kts | 22 + .../javaagent/plugin/sdk/CallDepth.java | 66 + .../plugin/sdk/CallDepthThreadLocal.java | 51 + .../javaagent/plugin/sdk/cache/Cache.java | 54 + .../plugin/sdk/cache/impl/MapBasedCache.java | 62 + .../plugin/sdk/cache/impl/WeakCache.java | 101 + .../sdk/constant/GlobalTracerName.java} | 15 +- .../sdk/field/InstrumentationStaticField.java | 90 + .../plugin/sdk/field/PluginSupportField.java} | 30 +- .../plugin/sdk/field/map/FieldMap.java} | 44 +- .../plugin/sdk/field/map/LockFieldMap.java | 128 + .../sdk/field/map/LockFreeFieldMap.java | 82 + .../sdk/instumentation/Instrumentor.java | 188 + .../plugin/sdk/instumentation/SpanScope.java | 40 + .../http/HttpInstrumentationSupport.java | 64 + .../javaagent/plugin/sdk/CallDepthTest.java | 39 + .../sdk/instumentation/SpanScopeTest.java | 27 + java-apm-agent/plugin/build.gradle | 28 - java-apm-agent/plugin/build.gradle.kts | 29 + .../plugin/hibernate/build.gradle.kts | 18 + .../HibernateInstrumentationHelper.java | 95 + .../HibernateQueryInstrumentation.java | 125 + ...ibernateSessionFactoryInstrumentation.java | 95 + .../HibernateSessionInstrumentation.java | 217 ++ .../HibernateTransactionInstrumentation.java | 101 + .../hibernate/helper/EntityNameHooker.java | 83 + .../plugin/hibernate/helper/SessionInfo.java | 54 + ...nt.extension.AbstractPluginInstrumentation | 4 + .../plugin/http-client/build.gradle.kts | 17 + .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../plugin/http/BodyHandlerWrapper.java | 104 + .../plugin/http/CompletableFutureWrapper.java | 51 + .../http/HttpClientInstrumentationHelper.java | 112 + .../http/HttpClientPluginInstrumentation.java | 147 + .../HttpHeadersPluginInstrumentation.java | 78 + .../plugin/http/HttpHeadersSetter.java | 70 + .../javaagent/plugin/http/HttpSpec.java | 51 + .../plugin/http/ResponseBiConsumer.java | 46 + ...nt.extension.AbstractPluginInstrumentation | 2 + .../http-url-connection/build.gradle.kts | 17 + .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 2 +- ...ttpUrlConnectionInstrumentationHelper.java | 85 + ...ttpUrlConnectionPluginInstrumentation.java | 104 + ...nt.extension.AbstractPluginInstrumentation | 1 + java-apm-agent/plugin/jdbc/build.gradle | 24 - java-apm-agent/plugin/jdbc/build.gradle.kts | 17 + .../jdbc/ConnectionPluginInstrumentation.java | 49 +- .../jdbc/DriverPluginInstrumentation.java | 97 + .../jdbc/JdbcInstrumentationHelper.java | 81 + ...PrepareStatementPluginInstrumentation.java | 55 +- .../jdbc/StatementPluginInstrumentation.java | 45 +- .../helper/JdbcInstrumentationHelper.java | 95 - .../plugin/jdbc/library/DatabaseInfo.java | 161 + .../plugin/jdbc/library/DatabaseRequest.java | 104 + .../plugin/jdbc/library/JdbcData.java | 71 + .../jdbc/library/JdbcInformationParser.java | 222 ++ .../javaagent/plugin/jdbc/util/JdbcUtil.java | 4 + ...t.extension.AbstractPluginInstrumentation} | 3 +- .../helper/JdbcInformationParserTest.java | 35 + .../plugin/logback/build.gradle.kts | 18 + .../logback/LogbackPluginInstrumentation.java | 95 + .../LogbackPluginInstrumentationHelper.java | 111 + ...nt.extension.AbstractPluginInstrumentation | 1 + java-apm-agent/plugin/logger/build.gradle.kts | 17 + .../plugin/logger/javaagent/build.gradle.kts | 18 + .../logger/LoggerPluginInstrumentation.java | 99 + .../LoggerPluginInstrumentationHelper.java | 122 + ...nt.extension.AbstractPluginInstrumentation | 1 + .../shaded-logging-module/build.gradle.kts | 17 + .../java/util/logging/Logger.java | 42 + java-apm-agent/plugin/plugin-sdk/build.gradle | 17 - .../plugin/servlet/build.gradle.kts | 18 + .../servlet/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../ServletFilterPluginInstrumentation.java | 117 + .../servlet/ServletPluginInstrumentation.java | 117 + .../ServletPluginInstrumentationHelper.java | 64 + ...nt.extension.AbstractPluginInstrumentation | 1 + .../spring-auto-configure/build.gradle.kts | 28 + .../rest/RestClientBeanPostProcessor.java | 61 + ...ngWebInstrumentationAutoConfiguration.java | 43 + .../SpringWebInstrumentation.java | 94 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...bInstrumentationAutoConfigurationTest.java | 42 + .../plugin/spring-business/build.gradle.kts | 17 + .../SpringBusinessInstrumentationHelper.java | 46 + ...ringBusinessRepositoryInstrumentation.java | 63 + .../SpringBusinessServiceInstrumentation.java | 65 + ...nt.extension.AbstractPluginInstrumentation | 1 + .../plugin/spring-webmvc/build.gradle | 22 - .../plugin/spring-webmvc/build.gradle.kts | 19 + .../webmvc/SpringWebMvcInstrumentation.java | 28 +- .../SpringWebMvcInstrumentationHelper.java | 70 + .../SpringWebMvcInstrumentationHelper.java | 68 - ...t.extension.AbstractPluginInstrumentation} | 0 .../{versions.gradle => versions.gradle.kts} | 0 server/build.gradle | 115 +- server/src/docs/asciidoc/alarm.adoc | 32 + server/src/docs/asciidoc/api-guide.adoc | 20 + server/src/docs/asciidoc/api-guide.html | 3149 +++++++++++++++++ server/src/docs/asciidoc/member.adoc | 45 + server/src/docs/asciidoc/sse.adoc | 21 + server/src/docs/asciidoc/statistics.adoc | 36 + .../TrafficHunterApplication.java | 3 +- .../traffic_hunter/common/aop/lock/Lock.java | 4 +- .../common/aop/lock/aspect/LockingAspect.java | 27 +- .../common/aop/lock/method/LockMode.java | 6 +- .../exception/TrafficHunterException.java | 2 +- .../exception/UnauthorizedException.java | 35 + .../common/map/AgentMapper.java | 2 +- .../traffic_hunter/common/map/LogMapper.java | 37 + .../common/map/SystemInfoMapper.java | 4 +- .../common/map/TransactionMapper.java | 2 +- .../map/impl/agent/AgentMapperImpl.java | 2 +- .../common/map/impl/log/LogMapperImpl.java | 70 + .../map/impl/system/SystemInfoMapperImpl.java | 13 +- .../transaction/TransactionMapperImpl.java | 9 +- .../common/util/LoginUtils.java | 5 +- .../common/util/MatcherUtil.java | 45 + .../web/interceptor/LoginInterceptor.java | 70 + .../RequestMatcherInterceptor.java | 118 + .../web/interceptor/RequestPattern.java | 41 + .../web/resolver/MemberArgumentResolver.java | 54 + .../config/async/AsyncConfig.java | 2 +- .../config/cache/CacheConfig.java | 17 +- .../config/lock/LockConfig.java | 5 +- .../config/mapper/ObjectMapperConfig.java | 2 +- .../config/rest/RestClientConfig.java | 46 + .../traffic_hunter/config/sse/SseConfig.java | 45 + .../traffic_hunter/config/web/WebConfig.java | 67 + .../config/web/webhook/WebHookConfig.java | 41 + .../{ => web}/websocket/WebSocketConfig.java | 4 +- .../ygo/traffic_hunter/core/alarm/Alarm.java | 33 + .../core/alarm/AlarmManager.java | 70 + .../core/alarm/WebHookAlarm.java | 39 + .../core/alarm/loss/LossPreventionHooker.java | 33 + .../AlarmPersistenceLossPreventionHooker.java | 68 + .../core/alarm/message/Message.java | 156 + .../core/alarm/message/MessageType.java | 160 + .../core/alarm/message/SseMessage.java | 49 + .../alarm/message/library/MessageMaker.java | 141 + .../core/annotation/Collector.java | 2 +- .../core/annotation/Member.java | 38 + .../core/annotation/Processor.java | 2 +- .../core/annotation/Validator.java | 2 +- .../core/assembler/Assembler.java | 33 + .../core/assembler/span/SpanAssembler.java | 77 + .../core/assembler/span/SpanTreeNode.java | 61 + .../core/collector/MetricCollector.java | 7 +- .../core/collector/channel/MetricChannel.java | 26 +- .../collector/channel/log/LogChannel.java | 71 + .../systeminfo/SysteminfoMetricChannel.java | 22 +- .../transaction/TransactionMetricChannel.java | 12 +- .../collector/processor/MetricProcessor.java | 36 +- .../compress/ByteArrayMetricDecompressor.java | 7 +- .../collector/validator/MetricValidator.java | 32 +- .../dto/request/alarm/AlarmThreshold.java | 43 + .../dto/request/alarm/ThresholdRequest.java | 54 + .../core/dto/request/member/SignIn.java | 42 + .../core/dto/request/member/SignUp.java | 42 + .../core/dto/request/member/UpdateMember.java | 44 + .../dto/request/metadata/AgentMetadata.java | 35 +- .../dto/request/metadata/AgentStatus.java | 35 +- .../dto/request/metadata/MetadataWrapper.java | 35 +- .../request/statistics/StatisticsRequest.java | 29 + .../dto/request/systeminfo/SystemInfo.java | 2 +- .../request/systeminfo/cpu/CpuStatusInfo.java | 2 +- .../systeminfo/dbcp/HikariDbcpInfo.java | 2 +- .../gc/GarbageCollectionStatusInfo.java | 2 +- .../gc/collections/GarbageCollectionTime.java | 2 +- .../systeminfo/memory/MemoryStatusInfo.java | 2 +- .../systeminfo/runtime/RuntimeStatusInfo.java | 2 +- .../systeminfo/thread/ThreadStatusInfo.java | 2 +- .../web/tomcat/TomcatWebServerInfo.java | 2 +- .../web/tomcat/request/TomcatRequestInfo.java | 2 +- .../tomcat/thread/TomcatThreadPoolInfo.java | 2 +- .../request/transaction/TransactionInfo.java | 2 +- .../response/RealTimeMonitoringResponse.java | 47 + .../response/TransactionMetricResponse.java | 49 +- .../dto/response/alarm/ActivationWebhook.java | 28 + .../dto/response/alarm/AlarmResponse.java | 38 + .../response/alarm/DeadLetterResponse.java | 33 + .../dto/response/alarm/ThresholdResponse.java | 44 + .../dto/response/member/MemberResponse.java | 50 + .../metric/CpuMetricMeasurementResponse.java | 35 + .../metric/HikariCPMeasurementResponse.java | 37 + .../MemoryMetricMeasurementResponse.java | 31 + .../metric/MemoryMetricUsageResponse.java | 39 + .../response/metric/MetricDataResponse.java | 42 + .../{ => metric}/SystemMetricResponse.java | 16 +- .../ThreadMetricMeasurementResponse.java | 37 + .../TomcatWebServerMeasurementResponse.java | 35 + ...atWebServerRequestMeasurementResponse.java | 37 + ...ebServerThreadPoolMeasurementResponse.java | 35 + .../metric/StatisticsMetricAvgResponse.java | 50 + .../metric/StatisticsMetricMaxResponse.java | 50 + .../ServiceTransactionResponse.java | 50 + .../core/event/channel/AlarmEvent.java | 34 + .../event/channel/ChannelEventHandler.java | 150 +- .../core/event/channel/LogEvent.java | 34 + .../event/channel/SystemInfoMetricEvent.java | 2 +- .../event/channel/TransactionMetricEvent.java | 2 +- .../core/identification/Identification.java | 35 + .../identification/SessionIdentification.java | 18 + .../identification/login/LoginHandler.java | 37 + .../session/SessionBasedLoginHandler.java | 61 + .../core/repository/AgentRepository.java | 2 +- .../core/repository/AlarmRepository.java | 63 + .../core/repository/MemberRepository.java | 52 + .../core/repository/MetricRepository.java | 36 +- .../core/schedule/Scheduler.java | 56 + .../ThreadPoolTaskSchedulerFactory.java | 64 + .../traffic_hunter/core/send/AlarmSender.java | 54 +- .../traffic_hunter/core/send/AsyncSender.java | 33 + .../traffic_hunter/core/send/ViewSender.java | 43 + .../core/service/AlarmService.java | 106 + .../core/service/MemberService.java | 121 + .../core/service/MetricService.java | 129 +- .../core/service/MetricStatisticsService.java | 77 + .../ygo/traffic_hunter/core/sse/Client.java | 70 + .../core/sse/ServerSentEventManager.java | 197 +- .../alert/ServerSentEventAlertManager.java | 46 - .../sse/view/ServerSentEventViewManager.java | 150 - .../statistics/StatisticsMetricTimeRange.java | 58 + .../traffic_hunter/core/webhook/Webhook.java | 28 + .../core/webhook/discord/DiscordWebHook.java | 103 + .../webhook/property/WebHookProperties.java | 34 + .../core/webhook/slack/SlackWebHook.java | 140 + .../traffic_hunter/domain/entity/Agent.java | 2 +- .../domain/entity/LogMeasurement.java | 41 + .../domain/entity/MetricMeasurement.java | 2 +- .../domain/entity/TransactionMeasurement.java | 2 +- .../domain/entity/alarm/Alarm.java | 38 + .../domain/entity/alarm/DeadLetter.java | 57 + .../domain/entity/alarm/Threshold.java | 158 + .../domain/entity/user/Member.java | 76 + .../domain/entity/user/Role.java | 33 + .../domain/interval/TimeInterval.java | 31 +- .../domain/metric/LogRecord.java | 53 + .../domain/metric/MetricData.java | 2 +- .../domain/metric/TraceInfo.java | 7 +- .../domain/metric/TransactionData.java | 11 +- .../metric/cpu/CpuMetricMeasurement.java | 2 +- .../dbcp/hikari/HikariCPMeasurement.java | 2 +- .../domain/metric/gc/GCMetricMeasurement.java | 2 +- .../gc/time/GCMetricCollectionTime.java | 2 +- .../memory/MemoryMetricMeasurement.java | 2 +- .../memory/usage/MemoryMetricUsage.java | 2 +- .../runtime/RuntimeMetricMeasurement.java | 2 +- .../thread/ThreadMetricMeasurement.java | 2 +- .../tomcat/TomcatWebServerMeasurement.java | 2 +- .../TomcatWebServerRequestMeasurement.java | 2 +- .../TomcatWebServerThreadPoolMeasurement.java | 2 +- .../persistence/impl/AlarmRepositoryImpl.java | 242 ++ .../impl/MemberRepositoryImpl.java | 226 ++ .../impl/TimeSeriesRepository.java | 441 ++- .../persistence/mapper/AgentRowMapper.java | 2 +- .../mapper/LogMeasurementRowMapper.java | 44 + .../persistence/mapper/RowMapSupport.java | 29 +- .../mapper/SystemMeasurementRowMapper.java | 74 +- .../mapper/TransactionDataMapper.java | 63 + .../TransactionMeasurementRowMapper.java | 39 +- .../StatisticsMetricAvgRowMapper.java | 52 + .../StatisticsMetricMaxRowMapper.java | 52 + ...StatisticsServiceTransactionRowMapper.java | 51 + .../persistence/query/QuerySupport.java | 49 + .../advice/GlobalControllerAdvice.java | 124 + .../advice/MetricControllerAdvice.java | 68 - .../controller/AlarmController.java | 99 + .../controller/MemberController.java | 116 + .../controller/MetricController.java | 82 +- ...er.java => ServerSentEventController.java} | 23 +- server/src/main/resources/application.yml | 5 + server/src/main/resources/schema.sql | 137 +- .../common/map/TransactionMapperTest.java | 8 +- .../common/util/MatcherUtilTest.java | 62 + .../RequestMatcherInterceptorTest.java | 70 + .../web/interceptor/RequestPatternTest.java | 33 + .../config/cache/CacheConfigTest.java | 33 + .../core/alarm/AlarmManagerTest.java | 97 + .../assembler/span/SpanAssemblerTest.java | 46 + .../core/collector/MetricCollectorTest.java | 27 + .../core/repository/MetricRepositoryTest.java | 18 +- .../service/MetricStatisticsServiceTest.java | 78 + .../core/sse/ServerSentEventManagerTest.java | 109 + .../webhook/discord/DiscordWebHookTest.java | 79 + .../core/webhook/slack/SlackWebHookTest.java | 79 + .../domain/entity/alarm/ThresholdTest.java | 73 + .../persistence/jooq/JooqQueryTest.java | 19 + .../persistence/mapper/RowMapSupportTest.java | 30 + .../controller/AlarmControllerTest.java | 252 ++ .../controller/MemberControllerTest.java | 358 ++ .../controller/MetricControllerTest.java | 371 ++ .../ServerSentEventControllerTest.java | 314 ++ settings.gradle | 14 +- .../ygo/testapp/config/RestClientConfig.java | 49 + .../testapp/controller/TestController.java | 13 + .../ygo/testapp/service/TestCallService.java | 54 + .../java/ygo/testapp/service/TestService.java | 3 + .../testapp/service/TestCallServiceTest.java | 28 + 400 files changed, 21177 insertions(+), 2509 deletions(-) create mode 100644 .github/release-drafter-config.yml create mode 100644 doc/image/log.png create mode 100644 doc/image/trace.jpeg delete mode 100644 java-apm-agent/build.gradle create mode 100644 java-apm-agent/build.gradle.kts delete mode 100644 java-apm-agent/java-agent-bootstrap/build.gradle create mode 100644 java-apm-agent/java-agent-bootstrap/build.gradle.kts create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/TrafficHunterAgentMain.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentExecutionEngine.java rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/{engine/instrument/locator => }/AgentLocator.java (90%) rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/{engine/instrument/bootstrap => }/BootState.java (95%) create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapLogger.java rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/{BootstrapMain.java => Configurations.java} (54%) create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/InstrumentationHolder.java rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/{engine/lifecycle => }/LifeCycle.java (97%) create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/OpenTelemetrySdkBridge.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentClassLoader.java rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/{engine => }/TrafficHunterAgentShutdownHook.java (90%) create mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentStarter.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java delete mode 100644 java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/BootstrapLoggerTest.java create mode 100644 java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/ConfigurationsTest.java delete mode 100644 java-apm-agent/java-agent-commons/build.gradle create mode 100644 java-apm-agent/java-agent-commons/build.gradle.kts create mode 100644 java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/type/MetricType.java delete mode 100644 java-apm-agent/java-agent-event/build.gradle create mode 100644 java-apm-agent/java-agent-event/build.gradle.kts create mode 100644 java-apm-agent/java-agent-extension/build.gradle.kts rename java-apm-agent/{plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/AbstractPluginInstrumentation.java (64%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/AgentExecutableContext.java (90%) create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/ConfigurableContextInitializer.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/LogRecord.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryManager.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryParser.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/PluginToolClassLoader.java rename java-apm-agent/{java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/TraceInfo.java (90%) rename java-apm-agent/{java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/TraceQueue.java (94%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/TrafficHunterAgentExecutableContext.java (80%) create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TrafficHunterAgentStartAction.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/Transformer.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/Utilizr.java rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/banner/AsciiBanner.java (86%) create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/bytebuddy/AdjustTransformer.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/bytebuddy/AgentIgnoreMatcher.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/bytebuddy/AgentLocationStrategy.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/bytebuddy/ByteBuddyLogger.java rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/ConfigurableEnvironment.java (88%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/Environment.java (96%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/YamlConfigurableEnvironment.java (87%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/bind/RelaxedBindingUtils.java (90%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/root/RootYamlProperty.java (90%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/root/agent/AgentSubProperty.java (94%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/root/agent/retry/RetrySubProperty.java (91%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/env/yaml/root/agent/retry/backoff/BackOffSubProperty.java (95%) create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/exporter/thunter/TrafficHunterLogExporter.java create mode 100644 java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/exporter/thunter/TrafficHunterSpanExporter.java rename java-apm-agent/{java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/exporter/TraceExporter.java => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/exporter/zipkin/ZipkinSpanExportDelegator.java} (57%) rename java-apm-agent/{plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/loader/PluginLoader.java (96%) rename java-apm-agent/{plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/loader/TrafficHunterPluginLoader.java (92%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/metadata/AgentMetadata.java (98%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/metadata/MetadataWrapper.java (96%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/property/TrafficHunterAgent.java (93%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/property/TrafficHunterAgentProperty.java (98%) rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine => java-agent-extension/src/main/java/org/traffichunter/javaagent/extension}/property/child/FaultTolerantTrafficHunterAgent.java (93%) rename java-apm-agent/{java-agent-bootstrap => java-agent-extension}/src/main/resources/agent-banner.txt (95%) delete mode 100644 java-apm-agent/java-agent-jmx/build.gradle create mode 100644 java-apm-agent/java-agent-jmx/build.gradle.kts rename java-apm-agent/java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/{collect/MetricCollectSupport.java => JmxMetricSender.java} (74%) delete mode 100644 java-apm-agent/java-agent-retry/build.gradle create mode 100644 java-apm-agent/java-agent-retry/build.gradle.kts create mode 100644 java-apm-agent/java-agent-retry/src/main/java/org/traffichunter/javaagent/retry/executor/RetryExecutor.java delete mode 100644 java-apm-agent/java-agent-trace/build.gradle delete mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/helper/AbstractSpanHelper.java delete mode 100644 java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/manager/TraceManager.java delete mode 100644 java-apm-agent/java-agent-websocket/build.gradle create mode 100644 java-apm-agent/java-agent-websocket/build.gradle.kts create mode 100644 java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/TrafficHunterWebsocketClient.java create mode 100644 java-apm-agent/java-agent-websocket/src/main/java/org/traffichunter/javaagent/websocket/property/WebSocketRetryProperty.java create mode 100644 java-apm-agent/java-agent-websocket/src/test/java/org/traffichunter/javaagent/websocket/converter/SerializationByteArrayConverterTest.java create mode 100644 java-apm-agent/java-agent/build.gradle.kts create mode 100644 java-apm-agent/plugin-sdk/build.gradle.kts create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/CallDepth.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/CallDepthThreadLocal.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/cache/Cache.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/cache/impl/MapBasedCache.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/cache/impl/WeakCache.java rename java-apm-agent/{plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/PluginConstant.java => plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/constant/GlobalTracerName.java} (88%) create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/field/InstrumentationStaticField.java rename java-apm-agent/{java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/MetricSender.java => plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/field/PluginSupportField.java} (60%) rename java-apm-agent/{java-agent-jmx/src/main/java/org/traffichunter/javaagent/jmx/collect/MetricCollector.java => plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/field/map/FieldMap.java} (60%) create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/field/map/LockFieldMap.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/field/map/LockFreeFieldMap.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instumentation/Instrumentor.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instumentation/SpanScope.java create mode 100644 java-apm-agent/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/support/http/HttpInstrumentationSupport.java create mode 100644 java-apm-agent/plugin-sdk/src/test/java/org/traffichunter/javaagent/plugin/sdk/CallDepthTest.java create mode 100644 java-apm-agent/plugin-sdk/src/test/java/org/traffichunter/javaagent/plugin/sdk/instumentation/SpanScopeTest.java delete mode 100644 java-apm-agent/plugin/build.gradle create mode 100644 java-apm-agent/plugin/build.gradle.kts create mode 100644 java-apm-agent/plugin/hibernate/build.gradle.kts create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/HibernateInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/HibernateQueryInstrumentation.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/HibernateSessionFactoryInstrumentation.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/HibernateSessionInstrumentation.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/HibernateTransactionInstrumentation.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/helper/EntityNameHooker.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/java/org/traffichunter/javaagent/plugin/hibernate/helper/SessionInfo.java create mode 100644 java-apm-agent/plugin/hibernate/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/http-client/build.gradle.kts rename java-apm-agent/{java-agent-trace => plugin/http-client}/gradle/wrapper/gradle-wrapper.jar (100%) rename java-apm-agent/{java-agent-trace => plugin/http-client}/gradle/wrapper/gradle-wrapper.properties (87%) create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/BodyHandlerWrapper.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/CompletableFutureWrapper.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/HttpClientInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/HttpClientPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/HttpHeadersPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/HttpHeadersSetter.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/HttpSpec.java create mode 100644 java-apm-agent/plugin/http-client/src/main/java/org/traffichunter/javaagent/plugin/http/ResponseBiConsumer.java create mode 100644 java-apm-agent/plugin/http-client/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/http-url-connection/build.gradle.kts rename java-apm-agent/plugin/{jdbc => http-url-connection}/gradle/wrapper/gradle-wrapper.jar (100%) rename java-apm-agent/plugin/{jdbc => http-url-connection}/gradle/wrapper/gradle-wrapper.properties (87%) create mode 100644 java-apm-agent/plugin/http-url-connection/src/main/java/org/traffichunter/javaagent/plugin/http/HttpUrlConnectionInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/http-url-connection/src/main/java/org/traffichunter/javaagent/plugin/http/HttpUrlConnectionPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/http-url-connection/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation delete mode 100644 java-apm-agent/plugin/jdbc/build.gradle create mode 100644 java-apm-agent/plugin/jdbc/build.gradle.kts create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/DriverPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/JdbcInstrumentationHelper.java delete mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/library/DatabaseInfo.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/library/DatabaseRequest.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/library/JdbcData.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/library/JdbcInformationParser.java create mode 100644 java-apm-agent/plugin/jdbc/src/main/java/org/traffichunter/javaagent/plugin/jdbc/util/JdbcUtil.java rename java-apm-agent/plugin/jdbc/src/main/resources/META-INF/services/{org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation => org.traffichunter.javaagent.extension.AbstractPluginInstrumentation} (73%) create mode 100644 java-apm-agent/plugin/jdbc/src/test/java/org/traffichunter/javaagent/plugin/jdbc/helper/JdbcInformationParserTest.java create mode 100644 java-apm-agent/plugin/logback/build.gradle.kts create mode 100644 java-apm-agent/plugin/logback/src/main/java/org/traffichunter/javaagent/plugin/logback/LogbackPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/logback/src/main/java/org/traffichunter/javaagent/plugin/logback/LogbackPluginInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/logback/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/logger/build.gradle.kts create mode 100644 java-apm-agent/plugin/logger/javaagent/build.gradle.kts create mode 100644 java-apm-agent/plugin/logger/javaagent/src/main/java/org/traffichunter/javaagent/plugin/logger/LoggerPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/logger/javaagent/src/main/java/org/traffichunter/javaagent/plugin/logger/LoggerPluginInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/logger/javaagent/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/logger/shaded-logging-module/build.gradle.kts create mode 100644 java-apm-agent/plugin/logger/shaded-logging-module/src/main/java/traffichunter/java/util/logging/Logger.java delete mode 100644 java-apm-agent/plugin/plugin-sdk/build.gradle create mode 100644 java-apm-agent/plugin/servlet/build.gradle.kts create mode 100644 java-apm-agent/plugin/servlet/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-apm-agent/plugin/servlet/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-apm-agent/plugin/servlet/src/main/java/org/traffichunter/javaagent/plugin/servlet/ServletFilterPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/servlet/src/main/java/org/traffichunter/javaagent/plugin/servlet/ServletPluginInstrumentation.java create mode 100644 java-apm-agent/plugin/servlet/src/main/java/org/traffichunter/javaagent/plugin/servlet/ServletPluginInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/servlet/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation create mode 100644 java-apm-agent/plugin/spring-auto-configure/build.gradle.kts create mode 100644 java-apm-agent/plugin/spring-auto-configure/src/main/java/org/traffichunter/plugin/spring/autoconfigure/rest/RestClientBeanPostProcessor.java create mode 100644 java-apm-agent/plugin/spring-auto-configure/src/main/java/org/traffichunter/plugin/spring/autoconfigure/rest/SpringWebInstrumentationAutoConfiguration.java create mode 100644 java-apm-agent/plugin/spring-auto-configure/src/main/java/org/traffichunter/plugin/spring/insrumentation/SpringWebInstrumentation.java create mode 100644 java-apm-agent/plugin/spring-auto-configure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 java-apm-agent/plugin/spring-auto-configure/src/test/java/org/traffichunter/plugin/spring/autoconfigure/rest/SpringWebInstrumentationAutoConfigurationTest.java create mode 100644 java-apm-agent/plugin/spring-business/build.gradle.kts create mode 100644 java-apm-agent/plugin/spring-business/src/main/java/org/traffichunter/javaagent/plugin/spring/business/SpringBusinessInstrumentationHelper.java create mode 100644 java-apm-agent/plugin/spring-business/src/main/java/org/traffichunter/javaagent/plugin/spring/business/SpringBusinessRepositoryInstrumentation.java create mode 100644 java-apm-agent/plugin/spring-business/src/main/java/org/traffichunter/javaagent/plugin/spring/business/SpringBusinessServiceInstrumentation.java create mode 100644 java-apm-agent/plugin/spring-business/src/main/resources/META-INF/services/org.traffichunter.javaagent.extension.AbstractPluginInstrumentation delete mode 100644 java-apm-agent/plugin/spring-webmvc/build.gradle create mode 100644 java-apm-agent/plugin/spring-webmvc/build.gradle.kts create mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/SpringWebMvcInstrumentationHelper.java delete mode 100644 java-apm-agent/plugin/spring-webmvc/src/main/java/org/traffichunter/javaagent/plugin/spring/webmvc/helper/SpringWebMvcInstrumentationHelper.java rename java-apm-agent/plugin/spring-webmvc/src/main/resources/META-INF/services/{org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation => org.traffichunter.javaagent.extension.AbstractPluginInstrumentation} (100%) rename java-apm-agent/{versions.gradle => versions.gradle.kts} (100%) create mode 100644 server/src/docs/asciidoc/alarm.adoc create mode 100644 server/src/docs/asciidoc/api-guide.adoc create mode 100644 server/src/docs/asciidoc/api-guide.html create mode 100644 server/src/docs/asciidoc/member.adoc create mode 100644 server/src/docs/asciidoc/sse.adoc create mode 100644 server/src/docs/asciidoc/statistics.adoc create mode 100644 server/src/main/java/ygo/traffic_hunter/common/exception/UnauthorizedException.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/map/LogMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/map/impl/log/LogMapperImpl.java rename java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/PluginInstrumentation.java => server/src/main/java/ygo/traffic_hunter/common/util/LoginUtils.java (91%) create mode 100644 server/src/main/java/ygo/traffic_hunter/common/util/MatcherUtil.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/web/interceptor/LoginInterceptor.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/web/interceptor/RequestMatcherInterceptor.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/web/interceptor/RequestPattern.java create mode 100644 server/src/main/java/ygo/traffic_hunter/common/web/resolver/MemberArgumentResolver.java create mode 100644 server/src/main/java/ygo/traffic_hunter/config/rest/RestClientConfig.java create mode 100644 server/src/main/java/ygo/traffic_hunter/config/sse/SseConfig.java create mode 100644 server/src/main/java/ygo/traffic_hunter/config/web/WebConfig.java create mode 100644 server/src/main/java/ygo/traffic_hunter/config/web/webhook/WebHookConfig.java rename server/src/main/java/ygo/traffic_hunter/config/{ => web}/websocket/WebSocketConfig.java (95%) create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/Alarm.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/AlarmManager.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/WebHookAlarm.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/loss/LossPreventionHooker.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/loss/persistence/AlarmPersistenceLossPreventionHooker.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/message/Message.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/message/MessageType.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/message/SseMessage.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/alarm/message/library/MessageMaker.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/annotation/Member.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/assembler/Assembler.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/assembler/span/SpanAssembler.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/assembler/span/SpanTreeNode.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/collector/channel/log/LogChannel.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/alarm/AlarmThreshold.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/alarm/ThresholdRequest.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/member/SignIn.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/member/SignUp.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/member/UpdateMember.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/request/statistics/StatisticsRequest.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/RealTimeMonitoringResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/alarm/ActivationWebhook.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/alarm/AlarmResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/alarm/DeadLetterResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/alarm/ThresholdResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/member/MemberResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/CpuMetricMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/HikariCPMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/MemoryMetricMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/MemoryMetricUsageResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/MetricDataResponse.java rename server/src/main/java/ygo/traffic_hunter/core/dto/response/{ => metric}/SystemMetricResponse.java (80%) create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/ThreadMetricMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/TomcatWebServerMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/TomcatWebServerRequestMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/metric/TomcatWebServerThreadPoolMeasurementResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/statistics/metric/StatisticsMetricAvgResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/statistics/metric/StatisticsMetricMaxResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/dto/response/statistics/transaction/ServiceTransactionResponse.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/event/channel/AlarmEvent.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/event/channel/LogEvent.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/identification/Identification.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/identification/SessionIdentification.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/identification/login/LoginHandler.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/identification/login/session/SessionBasedLoginHandler.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/repository/AlarmRepository.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/repository/MemberRepository.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/schedule/Scheduler.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/schedule/ThreadPoolTaskSchedulerFactory.java rename java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/classloading/THAgentClassLoader.java => server/src/main/java/ygo/traffic_hunter/core/send/AlarmSender.java (56%) create mode 100644 server/src/main/java/ygo/traffic_hunter/core/send/AsyncSender.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/send/ViewSender.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/service/AlarmService.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/service/MemberService.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/service/MetricStatisticsService.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/sse/Client.java delete mode 100644 server/src/main/java/ygo/traffic_hunter/core/sse/alert/ServerSentEventAlertManager.java delete mode 100644 server/src/main/java/ygo/traffic_hunter/core/sse/view/ServerSentEventViewManager.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/statistics/StatisticsMetricTimeRange.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/webhook/Webhook.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/webhook/discord/DiscordWebHook.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/webhook/property/WebHookProperties.java create mode 100644 server/src/main/java/ygo/traffic_hunter/core/webhook/slack/SlackWebHook.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/LogMeasurement.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/alarm/Alarm.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/alarm/DeadLetter.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/alarm/Threshold.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/user/Member.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/entity/user/Role.java create mode 100644 server/src/main/java/ygo/traffic_hunter/domain/metric/LogRecord.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/impl/AlarmRepositoryImpl.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/impl/MemberRepositoryImpl.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/mapper/LogMeasurementRowMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/mapper/TransactionDataMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/mapper/statistics/StatisticsMetricAvgRowMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/mapper/statistics/StatisticsMetricMaxRowMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/mapper/statistics/StatisticsServiceTransactionRowMapper.java create mode 100644 server/src/main/java/ygo/traffic_hunter/persistence/query/QuerySupport.java create mode 100644 server/src/main/java/ygo/traffic_hunter/presentation/advice/GlobalControllerAdvice.java delete mode 100644 server/src/main/java/ygo/traffic_hunter/presentation/advice/MetricControllerAdvice.java create mode 100644 server/src/main/java/ygo/traffic_hunter/presentation/controller/AlarmController.java create mode 100644 server/src/main/java/ygo/traffic_hunter/presentation/controller/MemberController.java rename server/src/main/java/ygo/traffic_hunter/presentation/controller/{ServerSentEvnetController.java => ServerSentEventController.java} (79%) create mode 100644 server/src/test/java/ygo/traffic_hunter/common/util/MatcherUtilTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/common/web/interceptor/RequestMatcherInterceptorTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/common/web/interceptor/RequestPatternTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/config/cache/CacheConfigTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/alarm/AlarmManagerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/assembler/span/SpanAssemblerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/collector/MetricCollectorTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/service/MetricStatisticsServiceTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/sse/ServerSentEventManagerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/webhook/discord/DiscordWebHookTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/core/webhook/slack/SlackWebHookTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/domain/entity/alarm/ThresholdTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/persistence/jooq/JooqQueryTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/persistence/mapper/RowMapSupportTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/presentation/controller/AlarmControllerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/presentation/controller/MemberControllerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/presentation/controller/MetricControllerTest.java create mode 100644 server/src/test/java/ygo/traffic_hunter/presentation/controller/ServerSentEventControllerTest.java create mode 100644 test-app/src/main/java/ygo/testapp/config/RestClientConfig.java create mode 100644 test-app/src/main/java/ygo/testapp/service/TestCallService.java create mode 100644 test-app/src/test/java/ygo/testapp/service/TestCallServiceTest.java diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml new file mode 100644 index 00000000..e26fa49f --- /dev/null +++ b/.github/release-drafter-config.yml @@ -0,0 +1,26 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: 'Features' + label: 'enhancement' + - title: 'Fixes' + label: 'bug' + - title: 'Etcetera' + label: 'documentation' +change-template: '- $TITLE #$NUMBER @$AUTHOR ' +template: | + ## The changes in this version are as follows. + --- + $CHANGES +no-changes-template: 'No changes' +version-resolver: + major: + labels: + - '✨ major' + minor: + labels: + - '✨ minor' + patch: + labels: + - '- '✨ patch' + default: patch diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2824362b..3ce638b8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,10 +11,11 @@ on: push: branches: - 'v*' + - 'master' jobs: build: - runs-on: self-hosted + runs-on: ubuntu-latest permissions: contents: write @@ -45,7 +46,7 @@ jobs: server/build/libs/*.jar release: - runs-on: self-hosted + runs-on: ubuntu-latest needs: build permissions: contents: write @@ -96,3 +97,11 @@ jobs: artifacts: | **/traffic-hunter-*.jar + release-drafter: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + with: + config-name: release-drafter-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index c2065bc2..a424a549 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ out/ ### VS Code ### .vscode/ +gradle.properties diff --git a/README.md b/README.md index 53e014c0..71e5f182 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,18 @@ ## Latest Release 🚀 -The pre-release version is v1.0.0. +The release version is v1.1.0. +- 2025/04/25 - 1.1.0 version - 2024/12/30 - 1.0.0 version -### Notes +## Note + +About traffic-hunter v1.1.0 -- Agent that is v1.0.0 supports only `spring framework 6.x` and `boot 3.x` +- Not supported distributed trace +- We were unable to integrate observability due to the absence of Traffic Hunter Visualization. We will complete and provide the visualization shortly. +- For the time being, **Zipkin** should be used in an in-memory environment. If a **Zipkin** environment is already set up, feel free to use it. Otherwise, use the **in-memory** setup for simple performance testing. We will improve the convenience in the next version—stay tuned! ## Intro. Traffic-Hunter @@ -36,25 +41,47 @@ The pre-release version is v1.0.0. ## Overview 👀 -- **Application Health Check** - cpu, heap memory, thread, web server, dbcp (database connection pool) +Traffic-Hunter provides full observability support. While you currently need to build your traces using Zipkin, +we’ll soon introduce [**traffic‑hunter‑visualization**](https://github.com/traffic-hunter/traffic-hunter-visualization) to eliminate that inconvenience. + +- **Application Metric** - cpu, heap memory, thread, web server, dbcp (database connection pool) 스크린샷 2024-12-12 오후 10 52 04 -- **Transaction Logs** - Transaction logs track application requests in real-time, recording executed methods and related information. +- **Trace** - Trace track application requests in real-time, recording executed methods and related information. (Support zipkin) + +![log.png](doc/image/trace.jpeg) -스크린샷 2024-12-12 오후 10 55 37 +- **Logs** - Logs from multiple applications can be centrally managed to enable efficient logging. + +![trace.jpeg](doc/image/log.png) ## Version Compatibility 👏 -| Java Version | Agent | Server | Notes | -|--------------|-----------------|----------------|-------------------------------------------| -| 21 | ✅ Supported | ✅ Supported | Includes support for advanced features like virtual threads | +| Java Version | Agent | Server | Notes | +|--------------|------------------|----------------|-------| +| 21+ | ✅ Supported | ✅ Supported | | + ### Notes - Java versions below 21 are **not supported**. - For optimal performance and access to the latest features, use Java 21. - Ensure both the agent and the server are running compatible Java versions. +## Supported Instrumentation Plugin + +| Instrumentation Plugin | Version | Note | +|------------------------|----------|------------------------------------------------------| +| Servlet | 6.0.0+ | Jakarta servlet | +| Spring web-mvc | 6.2.0+ | | +| Spring boot | 3.2.0+ | Supported RestClient,
Not Supported RestTemplate | +| JDBC | Java 21+ | | +| HttpClient | Java 21+ | | +| HttpUrlConnection | Java 21+ | | +| Hibernate | 6.0.0+ | LinkageError Issue,
Not Support | +| Logger | Java 21+ | java.util.Logging | +| LogBack | 1.5.0+ | ch.qos.logback | + ## License ```text diff --git a/doc/image/log.png b/doc/image/log.png new file mode 100644 index 0000000000000000000000000000000000000000..9974bd7d4c31a12b4e62144a27694e09fa9a4707 GIT binary patch literal 286582 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
QWJW8NOZ#-we2(atg$r>Erwk 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
811A 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 z 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 dx7PoR&?Bk*XJUW#3^*1}K}O|WxsTvpS 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ÐSP&ai@;4RFP&Y1Mr^~ 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 Po4{M-KPLOzi5WDmswL+ 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ⓈGDn#;^- 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 YGIKxfy4mC*|8Z@zNhSQ_+UCuF0{^ypx&QzG literal 0 HcmV?d00001 diff --git a/java-apm-agent/build.gradle b/java-apm-agent/build.gradle deleted file mode 100644 index f4a5be5d..00000000 --- a/java-apm-agent/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent' -version = '0.0.1-SNAPSHOT' - -dependencies { -} - -jar { - enabled = false -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-apm-agent/build.gradle.kts b/java-apm-agent/build.gradle.kts new file mode 100644 index 00000000..275732ea --- /dev/null +++ b/java-apm-agent/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("java") +} + +group = "org.traffichunter.javaagent" +version = "0.0.1-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { +} + +tasks.jar { + enabled = false +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/build.gradle b/java-apm-agent/java-agent-bootstrap/build.gradle deleted file mode 100644 index 61a1f8b3..00000000 --- a/java-apm-agent/java-agent-bootstrap/build.gradle +++ /dev/null @@ -1,57 +0,0 @@ -plugins { - id 'java' - id 'com.github.johnrengelman.shadow' version '8.1.1' -} - -group = 'org.traffichunter.javaagent.bootstrap' -version = '1.0-SNAPSHOT' - -apply from: '../versions.gradle' - -repositories { - mavenCentral() -} - -shadowJar { - - manifest { - attributes( - 'Premain-Class': 'org.traffichunter.javaagent.bootstrap.BootstrapMain', - 'Can-Redefine-Classes': 'true', - 'Can-Retransform-Classes': 'true', - 'Permissions': 'all-permissions' - ) - } - - archiveFileName.set("traffic-hunter-agent-${AgentReleaseVersion}.jar") -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-apm-agent:java-agent-event') - implementation project(':java-apm-agent:java-agent-commons') - implementation project(':java-apm-agent:java-agent-websocket') - implementation project(':java-apm-agent:java-agent-trace') - implementation project(':java-apm-agent:java-agent-retry') - implementation project(':java-apm-agent:java-agent-jmx') - implementation project(':java-apm-agent:plugin:plugin-sdk') - - implementation 'io.opentelemetry:opentelemetry-api:1.45.0' - implementation 'io.opentelemetry:opentelemetry-sdk:1.45.0' - - implementation 'org.slf4j:slf4j-api:2.0.7' - implementation 'org.slf4j:slf4j-simple:2.0.7' - implementation 'org.yaml:snakeyaml:2.3' - implementation 'org.java-websocket:Java-WebSocket:1.5.7' - implementation 'io.github.resilience4j:resilience4j-retry:2.2.0' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.18.0' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0' - implementation 'net.bytebuddy:byte-buddy:1.15.5' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/build.gradle.kts b/java-apm-agent/java-agent-bootstrap/build.gradle.kts new file mode 100644 index 00000000..1b3a6878 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("java") +} + +group = "org.traffichunter.javaagent.bootstrap" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") + + implementation(project(":java-apm-agent:plugin-sdk")) +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/TrafficHunterAgentMain.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/TrafficHunterAgentMain.java new file mode 100644 index 00000000..8d9561e1 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/TrafficHunterAgentMain.java @@ -0,0 +1,119 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent; + +import java.io.File; +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import org.traffichunter.javaagent.bootstrap.AgentExecutionEngine; +import org.traffichunter.javaagent.bootstrap.BootState; + +/** + * Agent main entry point. + * @author yungwang-o + * @version 1.0.0 + */ +public class TrafficHunterAgentMain { + + private static final BootState STATE = new BootState(); + + public static void premain(String agentArgs, Instrumentation inst) { + start(inst); + } + + public static void agentmain(String agentArgs, Instrumentation inst) { + start(inst); + } + + private static void start(final Instrumentation inst) { + + final boolean success = STATE.start(); + if(!success) { + System.err.println("Failed to start agent"); + return; + } + + File agentFile = loadBootstrapJar(inst); + AgentExecutionEngine.run(agentFile, getSystemEnvConfig(), inst); + } + + private static File loadBootstrapJar(final Instrumentation inst) { + + ClassLoader classLoader = TrafficHunterAgentMain.class.getClassLoader(); + + if(classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + + URL url = classLoader.getResource( + TrafficHunterAgentMain.class.getName().replace('.', '/') + ".class" + ); + + try { + File agentFile = getAgentFile(url); + + JarFile agentJarFile = new JarFile(agentFile, false); + verifyManifestBootstrapJar(agentJarFile); + inst.appendToBootstrapClassLoaderSearch(agentJarFile); + + return agentFile; + } catch (IOException | URISyntaxException e) { + throw new IllegalStateException("Failed to load bootstrap jar", e); + } + } + + private static File getAgentFile(final URL url) throws URISyntaxException { + + if (url == null || !"jar".equals(url.getProtocol())) { + throw new IllegalStateException("Unable to find traffic hunter agent jar"); + } + + String resource = url.toURI().getSchemeSpecificPart(); + int protocolIndex = resource.indexOf(":"); + int resourceIndex = resource.indexOf("!/"); + if (protocolIndex == -1 || resourceIndex == -1) { + throw new IllegalStateException("could not get agent location from url " + url); + } + + String agentPath = resource.substring(protocolIndex + 1, resourceIndex); + return new File(agentPath); + } + + private static void verifyManifestBootstrapJar(final JarFile agentJarFile) throws IOException { + + Manifest manifest = agentJarFile.getManifest(); + + if(manifest.getMainAttributes().getValue("Premain-Class") == null) { + throw new IllegalStateException("This agent not load because Premain-Class manifest not present"); + } + } + + private static String getSystemEnvConfig() { + return "traffichunter.config"; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentExecutionEngine.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentExecutionEngine.java new file mode 100644 index 00000000..5687a8fd --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentExecutionEngine.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.io.File; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.util.jar.JarFile; + +/** + * This class is execution engine + * @author yungwang-o + * @version 1.0.0 + */ +public final class AgentExecutionEngine { + + private static final BootstrapLogger log = BootstrapLogger.getLogger(AgentExecutionEngine.class); + + private static final String CALL_AGENT_STARTER = + "org.traffichunter.javaagent.extension.TrafficHunterAgentStartAction"; + + private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); + + private final String agentArgs; + + private final Instrumentation inst; + + private final File agentBootstrapJar; + + private AgentExecutionEngine(final File agentBootstrapJar, final String args, final Instrumentation inst) { + this.inst = inst; + this.agentArgs = args; + this.agentBootstrapJar = agentBootstrapJar; + } + + private void run() { + + if(AgentExecutionEngine.class.getClassLoader() != null) { + throw new IllegalStateException("AgentExecutionEngine is not loaded in bootstrap class loader"); + } + + if(!shutdownHook.isEnabledShutdownHook()) { + shutdownHook.enableShutdownHook(); + } + + try { + + InstrumentationHolder.setInstrumentation(inst); + + File file = new File(agentBootstrapJar.getParent() + "/javaagent-extension.jar"); + + JarFile jarFile = new JarFile(file, false); + + inst.appendToSystemClassLoaderSearch(jarFile); + + TrafficHunterAgentStarter trafficHunterAgentStarter = startAgent(ClassLoader.getSystemClassLoader()); + + trafficHunterAgentStarter.start(inst, agentArgs); + } catch (Exception e) { + throw new IllegalStateException("Failed to start agent", e); + } + } + + public static void run(final File agentBootstrapJar, final String args, final Instrumentation inst) { + new AgentExecutionEngine(agentBootstrapJar, System.getProperty(args), inst).run(); + } + + // Agent starter invokes reflection + private TrafficHunterAgentStarter startAgent(final ClassLoader agentClassLoader) throws Exception { + + Class agentStartAction = agentClassLoader.loadClass(CALL_AGENT_STARTER); + + Constructor agentStartActionConstructor = agentStartAction.getDeclaredConstructor(TrafficHunterAgentShutdownHook.class); + + return (TrafficHunterAgentStarter) agentStartActionConstructor.newInstance(shutdownHook); + } + + // TODO: I'm considering separating the app and the agent. + private static ClassLoader getAgentClassLoader(final File agentFile) { + + return new TrafficHunterAgentClassLoader(agentFile); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentLocator.java similarity index 90% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java rename to java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentLocator.java index 452dbef3..2e3ea6bd 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/locator/AgentLocator.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/AgentLocator.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.locator; +package org.traffichunter.javaagent.bootstrap; import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; -import org.traffichunter.javaagent.bootstrap.BootstrapMain; +import org.traffichunter.javaagent.TrafficHunterAgentMain; /** * @author yungwang-o @@ -38,7 +38,7 @@ public class AgentLocator { public static File getAgentJarFile() throws URISyntaxException { - ProtectionDomain protectionDomain = BootstrapMain.class.getProtectionDomain(); + ProtectionDomain protectionDomain = TrafficHunterAgentMain.class.getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); if(codeSource == null) { throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain)); diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootState.java similarity index 95% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java rename to java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootState.java index bb93bd03..b48b0e68 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/bootstrap/BootState.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootState.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap; +package org.traffichunter.javaagent.bootstrap; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapLogger.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapLogger.java new file mode 100644 index 00000000..3763d2cf --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapLogger.java @@ -0,0 +1,178 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.lang.System.Logger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.ResourceBundle; +import java.util.function.Supplier; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +public final class BootstrapLogger implements Logger { + + private final Class clazz; + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private BootstrapLogger(final Class clazz) { + this.clazz = clazz; + } + + public static BootstrapLogger getLogger(final Class clazz) { + return new BootstrapLogger(clazz); + } + + @Override + public String getName() { + return clazz.getName(); + } + + @Override + public boolean isLoggable(final Logger.Level level) { + return true; + } + + @Override + public void log(final Logger.Level level, + final ResourceBundle bundle, + final String msg, + final Throwable thrown) { + // TODO: bundle.. + } + + @Override + public void log(final Logger.Level level, + final ResourceBundle bundle, + final String format, + final Object... params) { + // TODO: bundle.. + } + + @Override + public void log(final Logger.Level level, final String msg) { + if(isLoggable(level)) { + // TODO: consider SOP io performance... + System.out.println(format(level, msg, List.of())); + } + } + + @Override + public void log(final Logger.Level level, final Supplier msgSupplier) { + if(isLoggable(level)) { + System.out.println(format(level, msgSupplier.get(), List.of())); + } + } + + @Override + public void log(final Logger.Level level, final Object obj) { + if(isLoggable(level)) { + System.out.println(format(level, "", obj)); + } + } + + @Override + public void log(final Logger.Level level, final String msg, final Throwable thrown) { + if(isLoggable(level)) { + System.out.println(format(level, msg, List.of())); + if(thrown != null) { + thrown.printStackTrace(System.out); + } + } + } + + @Override + public void log(final Logger.Level level, final Supplier msgSupplier, final Throwable thrown) { + if(isLoggable(level)) { + System.out.println(format(level, msgSupplier.get(), List.of())); + if(thrown != null) { + thrown.printStackTrace(System.out); + } + } + } + + @Override + public void log(final Logger.Level level, final String format, final Object... params) { + if(isLoggable(level)) { + System.out.println(format(level, format, params)); + } + } + + public void info(final String message, final Object... args) { + log(Logger.Level.INFO, message, args); + } + + public void debug(final String message, final Object... args) { + log(Logger.Level.DEBUG, message, args); + } + + public void warn(final String message, final Object... args) { + log(Logger.Level.WARNING, message, args); + } + + public void error(final String message, final Object... args) { + log(Logger.Level.ERROR, message, args); + } + + public void trace(final String message, final Object... args) { + log(Logger.Level.TRACE, message, args); + } + + private String format(final Level level, final String message, final Object... args) { + + if(args == null || message == null) { + throw new IllegalStateException("null.."); + } + + if(args.length == 0) { + return getFormat(level, message); + } + + if((message.isEmpty() || message.isBlank())) { + return Arrays.toString(args); + } + + String replace = message.replace("%", "%%") + .replace("{}", "%s"); + + String msg = String.format(replace, args); + + return getFormat(level, msg); + } + + private String getFormat(final Level level, final String msg) { + + return String.format("%s [%s] - %s: %s", + LocalDateTime.now().format(DATE_TIME_FORMATTER), + level.name(), + clazz.getName(), + msg + ); + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/Configurations.java similarity index 54% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java rename to java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/Configurations.java index cd761694..2c2354bf 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/BootstrapMain.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/Configurations.java @@ -1,7 +1,7 @@ -/** +/* * The MIT License * - * Copyright (c) 2024 yungwang-o + * Copyright (c) 2024 traffic-hunter.org * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,36 +23,40 @@ */ package org.traffichunter.javaagent.bootstrap; -import java.lang.instrument.Instrumentation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.AgentExecutionEngine; -import org.traffichunter.javaagent.bootstrap.engine.env.Environment; -import org.traffichunter.javaagent.bootstrap.engine.instrument.bootstrap.BootState; - /** - * main. * @author yungwang-o - * @version 1.0.0 + * @version 1.1.0 */ -public class BootstrapMain { +public final class Configurations { - private static final Logger log = LoggerFactory.getLogger(BootstrapMain.class); + public static boolean debug(final ConfigProperty configProperty) { + return Boolean.getBoolean(configProperty.getConfig()); + } - private static final BootState STATE = new BootState(); + public static boolean banner(final ConfigProperty configProperty) { + return Boolean.getBoolean(configProperty.getConfig()); + } - public static void premain(String agentArgs, Instrumentation inst) { + public static String export(final ConfigProperty configProperty) { + return System.getProperty(configProperty.getConfig()); + } - final boolean success = STATE.start(); - if(!success) { - log.error("traffic-hunter-agent-bootstrap already started. skipping agent loading."); - return; - } + public enum ConfigProperty { - AgentExecutionEngine.run(Environment.SYSTEM_PROFILE.systemProfile(), inst); - } + TRANSFORM_DEBUG("traffichunter.javaagent.transform.debug"), + EXPORTER_DEBUG("traffichunter.javaagent.exporter.debug"), + BANNER_MODE("traffichunter.javaagent.banner"), + ZIPKIN_EXPORTER_ENDPOINT("traffichunter.javaagent.zipkin.endpoint"), + ; + + private final String config; - public static void agentmain(String agentArgs, Instrumentation inst) { - premain(agentArgs, inst); + ConfigProperty(final String config) { + this.config = config; + } + + public String getConfig() { + return config; + } } } diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/InstrumentationHolder.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/InstrumentationHolder.java new file mode 100644 index 00000000..455e6889 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/InstrumentationHolder.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.lang.instrument.Instrumentation; + +/** + * This class serves as an "everywhere accessible" + * @author yungwang-o + * @version 1.1.0 + */ +public class InstrumentationHolder { + + private static volatile Instrumentation INSTRUMENTATION; + + public static void setInstrumentation(final Instrumentation instrumentation) { + + if(INSTRUMENTATION != null) { + INSTRUMENTATION = instrumentation; + } + } + + public static Instrumentation getInstrumentation() { + return INSTRUMENTATION; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/LifeCycle.java similarity index 97% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java rename to java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/LifeCycle.java index 7fd7ea42..e0ea5ac0 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/lifecycle/LifeCycle.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/LifeCycle.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine.lifecycle; +package org.traffichunter.javaagent.bootstrap; import java.time.Duration; import java.time.Instant; diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/OpenTelemetrySdkBridge.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/OpenTelemetrySdkBridge.java new file mode 100644 index 00000000..6be5e47f --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/OpenTelemetrySdkBridge.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.util.concurrent.TimeUnit; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +public class OpenTelemetrySdkBridge { + + public interface ForceFlusher { + + void run(long timeout, TimeUnit unit); + } + + private static volatile ForceFlusher forceFlush; + + public static void forceFlush(final long timeout, final TimeUnit unit) { + forceFlush.run(timeout, unit); + } + + public static void setOpenTelemetrySdkForceFlush(final ForceFlusher forceFlusher) { + + if (OpenTelemetrySdkBridge.forceFlush == null) { + OpenTelemetrySdkBridge.forceFlush = forceFlusher; + } + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentClassLoader.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentClassLoader.java new file mode 100644 index 00000000..a1785bd9 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentClassLoader.java @@ -0,0 +1,356 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.security.CodeSource; +import java.security.Permission; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.Objects; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * This custom class loader quoted the opentelemetry agent class loader. + * @author yungwang-o + * @version 1.1.0 + */ +public class TrafficHunterAgentClassLoader extends URLClassLoader { + + private static final String JAVA_EXTENSION_JAR_URL = "javaagent-extension.jar"; + private static final String JAVA_PREFIX_ENTRY = "extension/"; + + private static final int MINIMUM_JAVA_VERSION = 17; + + // inst.appendBootstrapClassLoader + private final BootStrapClassLoderProxy bootstrapClassLoader; + + private final JarFile agentJarFile; + private final URL agentJarUrl; + private final CodeSource codeSource; + private final Manifest manifest; + + static { + + if (!ClassLoader.registerAsParallelCapable()) { + System.err.println("TrafficHunterAgentClassLoader not registered as parallel"); + } + } + + public TrafficHunterAgentClassLoader(final File javaagentFile) { + super(new URL[] {}, getSystemClassLoader()); + + Objects.requireNonNull(javaagentFile, "javaagentFile"); + + try { + + this.bootstrapClassLoader = new BootStrapClassLoderProxy(this); + + this.agentJarFile = new JarFile(javaagentFile, false); + this.agentJarUrl = new URL("file", null, 0, "/", SpecificClassLoaderURLStreamHandler.getStreamHandler(agentJarFile)); + this.codeSource = new CodeSource(javaagentFile.toURI().toURL(), (Certificate[]) null); + this.manifest = agentJarFile.getManifest(); + } catch (IOException e) { + throw new IllegalStateException("Agent file could not be loaded", e); + } + } + + @Override + protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(final String name) throws ClassNotFoundException { + JarEntry jarEntry = findAgentJarEntry(name.replace('.', '/') + ".class"); + + if(jarEntry == null) { + return null; + } + + try (InputStream is = agentJarFile.getInputStream(jarEntry)) { + int size = (int) jarEntry.getSize(); + int offset = 0; + int read; + + byte[] clazzBuf = new byte[size]; + + while (offset < size && (read = is.read(clazzBuf, offset, size - offset)) != -1) { + offset += read; + } + + checkDefinedPackage(name); + return defineClass(name, clazzBuf, 0, clazzBuf.length, codeSource); + } catch (IOException e) { + throw new ClassNotFoundException("agent class not found : " + name, e); + } + } + + private JarEntry findAgentJarEntry(final String name) { + + String prefixExtensionJarEntry = JAVA_PREFIX_ENTRY + name; + + return agentJarFile.getJarEntry(prefixExtensionJarEntry); + } + + private void checkDefinedPackage(final String clazz) { + String packageName = getPackageName(clazz); + + if(packageName == null) { + return; + } + + if (getDefinedPackage(packageName) == null) { + try { + definePackage(packageName, manifest, codeSource.getLocation()); + } catch (IllegalArgumentException e) { + if(getDefinedPackage(packageName) == null) { + throw new IllegalStateException("Package not define", e); + } + } + } + } + + @Override + public URL getResource(final String name) { + URL url = bootstrapClassLoader.getResource(name); + + if(url != null) { + return url; + } + + return super.getResource(name); + } + + @Override + public URL findResource(final String name) { + JarEntry jarEntry = findAgentJarEntry(name); + + URL entryUrl = getJarEntryUrl(jarEntry); + if (entryUrl != null) { + return entryUrl; + } + + return super.findResource(name); + } + + @Override + public Enumeration findResources(final String name) throws IOException { + Enumeration resources = super.findResources(name); + + JarEntry agentJarEntry = findAgentJarEntry(name); + + URL entryUrl = getJarEntryUrl(agentJarEntry); + if (entryUrl != null) { + return new Enumeration<>() { + boolean next = true; + + @Override + public boolean hasMoreElements() { + return next || resources.hasMoreElements(); + } + + @Override + public URL nextElement() { + if(next) { + next = false; + return entryUrl; + } + + return resources.nextElement(); + } + }; + } + + return resources; + } + + private URL getJarEntryUrl(final JarEntry jarEntry) { + if(jarEntry == null) { + return null; + } + + try { + return new URL(agentJarUrl, jarEntry.getName()); + } catch (MalformedURLException e) { + throw new IllegalStateException("Failed to generate jar entry url", e); + } + } + + private static String getPackageName(final String clazz) { + int idx = clazz.lastIndexOf("."); + + return idx == -1 ? null : clazz.substring(0, idx); + } + + private boolean checkJavaVersion() { + return MINIMUM_JAVA_VERSION <= getJavaVersion(); + } + + private static int getJavaVersion() { + String javaVersion = System.getProperty("java.specification.version"); + + return Integer.parseInt(javaVersion); + } + + public static final class BootStrapClassLoderProxy extends ClassLoader { + + private final TrafficHunterAgentClassLoader agentClassLoader; + + static { + ClassLoader.registerAsParallelCapable(); + } + + public BootStrapClassLoderProxy(final TrafficHunterAgentClassLoader agentClassLoader) { + super(null); + this.agentClassLoader = agentClassLoader; + } + + @Override + public URL getResource(final String name) { + + URL url = super.getResource(name); + if (url != null) { + return url; + } + + if (agentClassLoader != null) { + JarEntry jarEntry = agentClassLoader.agentJarFile.getJarEntry(name); + return agentClassLoader.getJarEntryUrl(jarEntry); + } + + return null; + } + } + + private static class SpecificClassLoaderURLStreamHandler extends URLStreamHandler { + + private final JarFile jarFile; + + SpecificClassLoaderURLStreamHandler(final JarFile jarFile) { + this.jarFile = jarFile; + } + + static URLStreamHandler getStreamHandler(final JarFile jarFile) { + return new SpecificClassLoaderURLStreamHandler(jarFile); + } + + @Override + protected URLConnection openConnection(final URL u) { + return new SpecificClassLoaderURLConnection(u, jarFile); + } + } + + private static class SpecificClassLoaderURLConnection extends URLConnection { + + private final JarFile jarFile; + private final String entryPath; + private JarEntry jarEntry; + + SpecificClassLoaderURLConnection(final URL url, final JarFile jarFile) { + super(url); + this.jarFile = jarFile; + String filePath = url.getFile(); + if(filePath.startsWith("/")) { + filePath = filePath.substring(1); + } + + if(filePath.isEmpty()) { + filePath = null; + } + + this.entryPath = filePath; + } + + @Override + public void connect() throws IOException { + if(!connected) { + if(entryPath != null) { + jarEntry = jarFile.getJarEntry(entryPath); + if(jarEntry == null) { + throw new FileNotFoundException("jar entry not found: " + entryPath); + } + } + + connected = true; + } + } + + @Override + public Permission getPermission() { + return null; + } + + @Override + public InputStream getInputStream() throws IOException { + connect(); + + if (entryPath == null) { + throw new IOException("no entry name specified"); + } else { + if (jarEntry == null) { + throw new FileNotFoundException( + "JAR entry " + entryPath + " not found in " + jarFile.getName()); + } + return jarFile.getInputStream(jarEntry); + } + } + + @Override + public long getContentLengthLong() { + try { + connect(); + + if (jarEntry != null) { + return jarEntry.getSize(); + } + } catch (IOException ignored) {} + + return -1; + } + + @Override + public int getContentLength() { + return super.getContentLength(); + } + } + + public BootStrapClassLoderProxy getBootstrapClassLoader() { + return bootstrapClassLoader; + } + + public JarFile getAgentJarFile() { + return agentJarFile; + } +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentShutdownHook.java similarity index 90% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java rename to java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentShutdownHook.java index 5bec461a..a535fde3 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/TrafficHunterAgentShutdownHook.java +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentShutdownHook.java @@ -21,12 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine; +package org.traffichunter.javaagent.bootstrap; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The {@code TrafficHunterAgentShutdownHook} class is responsible for managing and @@ -41,7 +39,7 @@ */ public class TrafficHunterAgentShutdownHook implements Runnable { - private static final Logger log = LoggerFactory.getLogger(TrafficHunterAgentShutdownHook.class); + private static final BootstrapLogger log = BootstrapLogger.getLogger(TrafficHunterAgentShutdownHook.class); private volatile boolean enabledShutdownHook = false; @@ -51,8 +49,9 @@ public void enableShutdownHook() { enabledShutdownHook = true; } - public void addRuntimeShutdownHook(final Runnable action) { + public TrafficHunterAgentShutdownHook addRuntimeShutdownHook(final Runnable action) { actions.add(action); + return this; } @Override diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentStarter.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentStarter.java new file mode 100644 index 00000000..284ad0b8 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/TrafficHunterAgentStarter.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.bootstrap; + +import java.lang.instrument.Instrumentation; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +public interface TrafficHunterAgentStarter { + + void start(Instrumentation inst, String envPath); + + ClassLoader getAgentStartClassLoader(); +} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java deleted file mode 100644 index 35597d21..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/AgentExecutionEngine.java +++ /dev/null @@ -1,238 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine; - -import java.lang.instrument.Instrumentation; -import java.time.Duration; -import java.time.Instant; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.banner.AsciiBanner; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.context.execute.TrafficHunterAgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.env.yaml.YamlConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.lifecycle.LifeCycle; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.manager.MetricSendSessionManager; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.trace.exporter.TraceExporter; -import org.traffichunter.javaagent.trace.manager.TraceManager; -import org.traffichunter.javaagent.trace.queue.TraceQueue; - -/** - *

- * The {@code AgentExecutionEngine} class is the core execution engine responsible for - * initializing, configuring, running, and shutting down the TrafficHunter Agent. - * This class encapsulates all the necessary steps to bootstrap the agent, manage its - * lifecycle, and ensure proper cleanup upon application termination. - *

- *

Purpose:

- *
    - *
  • Initialize and configure the TrafficHunter Agent with environment-specific settings.
  • - *
  • Run the agent in a dedicated thread to monitor and manage traffic.
  • - *
  • Register and manage shutdown hooks to ensure proper cleanup during termination.
  • - *
- * - *

Key Components:

- *
    - *
  • {@link TrafficHunterAgentShutdownHook} - Manages graceful shutdown of resources.
  • - *
  • {@link ConfigurableEnvironment} - Loads environment-specific configurations.
  • - *
  • {@link AgentRunner} - Executes the agent's core logic in a separate thread.
  • - *
- * - *

Limitations:

- *
    - *
  • This class assumes that the {@code Instrumentation} object is correctly provided at runtime.
  • - *
  • Agent initialization failures may result in partial cleanup if not handled carefully.
  • - *
- * - * @see TrafficHunterAgentShutdownHook - * @see AgentRunner - * @see ConfigurableEnvironment - * @see Instrumentation - * @author yungwang-o - * @version 1.0.0 - */ -public final class AgentExecutionEngine { - - private static final Logger log = LoggerFactory.getLogger(AgentExecutionEngine.class); - - private final TrafficHunterAgentShutdownHook shutdownHook = new TrafficHunterAgentShutdownHook(); - - private final AsciiBanner asciiBanner = new AsciiBanner(); - - private final ConfigurableEnvironment environment; - - private final Instrumentation inst; - - private AgentExecutionEngine(final String args, final Instrumentation inst) { - this.inst = inst; - this.environment = new YamlConfigurableEnvironment(args); - } - - /** - * Initializes and executes the TrafficHunter Agent. - *

This method performs the following tasks:

- *
    - *
  • Loads environment-specific configurations using {@link YamlConfigurableEnvironment}.
  • - *
  • Initializes the agent's execution context and metadata.
  • - *
  • Registers shutdown hooks for cleanup during termination.
  • - *
  • Starts the agent's core logic in a dedicated thread.
  • - *
- */ - private void run() { - StartUp startUp = new StartUp(); - Instant startTime = startUp.getStartTime(); - final AgentExecutableContext context = new TrafficHunterAgentExecutableContext(environment, shutdownHook); - if(!shutdownHook.isEnabledShutdownHook()) { - shutdownHook.enableShutdownHook(); - } - ConfigurableContextInitializer configurableContextInitializer = context.init(); - TraceManager traceManager = configurableContextInitializer.setTraceManager(new TraceExporter()); - configurableContextInitializer.retransform(inst); - TrafficHunterAgentProperty property = configurableContextInitializer.property(); - AgentMetadata metadata = configurableContextInitializer.setAgentMetadata( - startTime, - AgentStatus.INITIALIZED - ); - context.addAgentStateEventListener(metadata); - asciiBanner.print(metadata); - AgentRunner runner = new AgentRunner(property, context, metadata); - Thread runnerThread = new Thread(runner); - runnerThread.setName("TrafficHunterAgentRunnerThread"); - if(context.isInit()) { - log.info("Agent initialization completed."); - runnerThread.start(); - registryShutdownHook(context, runner, traceManager); - context.close(); - } - - log.info("Started TrafficHunter Agent in {} second", String.format("%.3f", startUp.getUpTime())); - } - - /** - * Registers shutdown hooks for the agent's cleanup operations. - * - * @param context The execution context of the agent. - * @param runner The agent runner instance responsible for managing execution. - * @param traceManager The trace manager manages a trace's lifecycle. - */ - private void registryShutdownHook(final AgentExecutableContext context, - final AgentRunner runner, - final TraceManager traceManager) { - - shutdownHook.addRuntimeShutdownHook(traceManager::close); - shutdownHook.addRuntimeShutdownHook(TraceQueue.INSTANCE::removeAll); - shutdownHook.addRuntimeShutdownHook(context::removeAllAgentStateEventListeners); - shutdownHook.addRuntimeShutdownHook(runner::close); - } - - public static void run(final String args, final Instrumentation inst) { - new AgentExecutionEngine(System.getProperty(args), inst).run(); - } - - /** - * The {@code StartUp} class extends {@link LifeCycle} to measure the agent's - * startup durations. - * - *

Features:

- *
    - *
  • Tracks the agent's start time and end time.
  • - *
  • Calculates the total uptime.
  • - *
- */ - private static class StartUp extends LifeCycle { - - public StartUp() { - super(); - } - - @Override - public Instant getStartTime() { - return this.startTime; - } - - @Override - public Instant getEndTime() { - if(endTime == null) { - this.endTime = Instant.now(); - } - return endTime; - } - - @Override - public Double getUpTime() { - if(getStartTime() == null && getEndTime() == null) { - throw new IllegalStateException("No start time or end time specified"); - } - - return Duration.between(getStartTime(), getEndTime()).toMillis() / 1_000.0; - } - } - - /** - * The {@code AgentRunner} class implements {@link Runnable} to execute the core logic - * of the TrafficHunter Agent in a separate thread. It initializes the session manager - * and orchestrates agent operations after the target application is loaded. - * - *

Features:

- *
    - *
  • Introduces a delay to ensure the target application is fully loaded before execution.
  • - *
  • Manages the lifecycle of the {@link MetricSendSessionManager}.
  • - *
- */ - private static final class AgentRunner implements Runnable { - - private final MetricSendSessionManager sessionManager; - - public AgentRunner(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.sessionManager = new MetricSendSessionManager(property, context, metadata); - } - - /** - * Executes the agent's core logic. Introduces a delay to ensure that - * the target application is fully loaded before starting. - */ - @Override - public void run() { - try { - log.info("Waiting for Agent Runner..."); - Thread.sleep(8000); - sessionManager.run(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - public void close() { - sessionManager.close(); - } - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java deleted file mode 100644 index b7d4cbc8..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/configuration/ConfigurableContextInitializer.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.context.configuration; - -import java.io.InputStream; -import java.lang.instrument.Instrumentation; -import java.time.Instant; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.agent.builder.AgentBuilder.Default; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; -import org.traffichunter.javaagent.bootstrap.engine.env.Environment; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.commons.util.UUIDGenerator; -import org.traffichunter.javaagent.plugin.sdk.instrumentation.AbstractPluginInstrumentation; -import org.traffichunter.javaagent.plugin.sdk.loader.PluginLoader; -import org.traffichunter.javaagent.plugin.sdk.loader.TrafficHunterPluginLoader; -import org.traffichunter.javaagent.trace.exporter.TraceExporter; -import org.traffichunter.javaagent.trace.manager.TraceManager; - -/** - * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, - * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. - * - *

Features:

- *
    - *
  • Loads properties using a configurable environment.
  • - *
  • Sets up agent metadata based on the current environment and runtime status.
  • - *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • - *
- * - * @see ConfigurableEnvironment - * @see TrafficHunterAgentProperty - * @see ByteBuddy - * @see AgentMetadata - * - * @author yungwang-o - * @version 1.0.0 - */ -public class ConfigurableContextInitializer { - - private static final Logger log = LoggerFactory.getLogger(ConfigurableContextInitializer.class); - - private final ConfigurableEnvironment env; - - public ConfigurableContextInitializer(final ConfigurableEnvironment env) { - this.env = env; - } - - public TrafficHunterAgentProperty property() { - return env.load(); - } - - public TrafficHunterAgentProperty property(final InputStream is) { - return env.load(is); - } - - public TraceManager setTraceManager(final TraceExporter exporter) { - return new TraceManager(exporter); - } - - /** - * Load all plugins to manipulate the target application's bytecode. - */ - public void retransform(final Instrumentation inst) { - - List plugins = - loadPlugins(ConfigurableContextInitializer.class.getClassLoader()); - - AgentBuilder.Default agentBuilder = new AgentBuilder.Default(); - - for(AbstractPluginInstrumentation plugin : plugins) { - agentBuilder = (Default) agentBuilder - .type(plugin.typeMatcher()) - .transform(plugin.transform()); - } - - agentBuilder.installOn(inst); - } - - public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { - - final String agentName = property().name(); - - return new AgentMetadata( - UUIDGenerator.generate(agentName), - Environment.VERSION.version(), - agentName, - startTime, - new AtomicReference<>(status) - ); - } - - private List loadPlugins(final ClassLoader classLoader) { - - PluginLoader pluginLoader = new TrafficHunterPluginLoader(); - - return pluginLoader.loadModules(classLoader); - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java deleted file mode 100644 index 1765afed..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/instrument/annotation/AnnotationPath.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.instrument.annotation; - -/** - * The {@code AnnotationPath} enum defines commonly used annotation paths - * for Spring-based applications, which are used for bytecode manipulation - * with ByteBuddy. - * - * @author yungwang-o - * @version 1.0.0 - */ -public enum AnnotationPath { - - TRANSACTIONAL("org.springframework.transaction.annotation.Transactional"), - SERVICE("org.springframework.stereotype.Service"), - REPOSITORY("org.springframework.stereotype.Repository"), - REST_CONTROLLER("org.springframework.web.bind.annotation.RestController"), - CONTROLLER("org.springframework.stereotype.Controller"), - ; - - private final String path; - - AnnotationPath(final String path) { - this.path = path; - } - - public String getPath() { - return path; - } - - public static boolean filter(final String path) { - return path.equals("join") || path.equals("wait") || path.equals("notify") || path.equals("notifyAll") || - path.equals("hashcode") || path.equals("equals") || path.equals("toString"); - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java deleted file mode 100644 index 45a0b07a..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/manager/MetricSendSessionManager.java +++ /dev/null @@ -1,198 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.manager; - -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryConfig; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentSystemMetricSender; -import org.traffichunter.javaagent.bootstrap.engine.sender.websocket.AgentTransactionMetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.commons.status.AgentStatus; -import org.traffichunter.javaagent.commons.util.AgentUtil; -import org.traffichunter.javaagent.retry.RetryHelper; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.metadata.Metadata; - -/** - * The {@code MetricSendSessionManager} class manages the session for sending - * transaction and system metrics from the TrafficHunter Agent to a server. - * It handles WebSocket connections, retries, scheduling, and lifecycle management - * of the metric-sending operations. - * - *

Purpose:

- *
    - *
  • Manages WebSocket connections for real-time metric transmission.
  • - *
  • Schedules periodic system metric transmissions using a {@link ScheduledExecutorService}.
  • - *
  • Retries connection attempts in case of failures with a configurable back-off policy.
  • - *
  • Provides lifecycle management for starting and stopping the session.
  • - *
- * - *

Key Features:

- *
    - *
  • {@code run()} - Starts the metric sending session, ensuring retries and scheduling are properly configured.
  • - *
  • {@code close()} - Safely shuts down the session, releasing all resources.
  • - *
  • Integrates with {@link Retry} to handle WebSocket reconnections in case of failures.
  • - *
- * - *

Thread Management:

- *
    - *
  • Uses a {@link ScheduledExecutorService} for scheduling periodic system metrics.
  • - *
  • Uses a {@link ExecutorService} for running transaction metric transmissions.
  • - *
  • Both executors are safely shut down during the {@code close()} method.
  • - *
- * - *

Limitations:

- *
    - *
  • If the agent's context status is already {@code RUNNING}, the session cannot be restarted without stopping it first.
  • - *
  • Retries only handle specific exceptions, as configured in the {@link RetryHelper}.
  • - *
- * - * @see Retry - * @see ScheduledExecutorService - * @see MetricWebSocketClient - * @see TrafficHunterAgentProperty - * @see AgentExecutableContext - * @see AgentMetadata - * @see RetryHelper - * @author yungwang-o - * @version 1.0.0 - */ -public class MetricSendSessionManager { - - private static final Logger log = LoggerFactory.getLogger(MetricSendSessionManager.class); - - private final TrafficHunterAgentProperty property; - - private final AgentTransactionMetricSender transactionMetricSender; - - private final AgentSystemMetricSender systemMetricSender; - - private final ScheduledExecutorService schedule; - - private final ExecutorService executor; - - private final AgentExecutableContext context; - - private final AgentMetadata metadata; - - private final MetricWebSocketClient client; - - public MetricSendSessionManager(final TrafficHunterAgentProperty property, - final AgentExecutableContext context, - final AgentMetadata metadata) { - - this.client = initializeWebsocket(property, metadata); - this.client.connect(); - this.metadata = metadata; - this.context = context; - this.property = property; - this.transactionMetricSender = new AgentTransactionMetricSender(client); - this.systemMetricSender = new AgentSystemMetricSender(client, property); - this.schedule = Executors.newSingleThreadScheduledExecutor(getThreadFactory("TransactionSystemInfoMetricSender")); - this.executor = Executors.newVirtualThreadPerTaskExecutor(); - } - - public void run() { - - if(context.isStopped()) { - log.error("MetricSendSessionManager is stopped"); - return; - } - - if(context.isRunning()) { - log.error("MetricSendSessionManager is already running"); - return; - } - - log.info("start Metric send!!"); - - RetryHelper retryHelper = RetryHelper.builder() - .backOffPolicy(property.backOffPolicy()) - .isCheck(true) - .retryName("websocket retry") - .maxAttempts(property.maxAttempt()) - .retryPredicate(throwable -> throwable instanceof IllegalStateException) - .build(); - - RetryConfig retryConfig = retryHelper.configureRetry(); - - Retry retry = Retry.of(retryHelper.getRetryName(), retryConfig); - - retry.getEventPublisher() - .onRetry(event -> { - client.reconnect(); - log.info("{} retry {} attempts...", event.getName(), event.getNumberOfRetryAttempts()); - }); - - context.setStatus(AgentStatus.RUNNING); - - executor.execute(Retry.decorateRunnable(retry, () -> transactionMetricSender.toSend(metadata))); - - schedule.scheduleWithFixedDelay(Retry.decorateRunnable(retry, () -> systemMetricSender.toSend(metadata)), - 0, - property.scheduleInterval(), - property.timeUnit() - ); - } - - public void close() { - log.info("closing MetricSendSessionManager..."); - context.setStatus(AgentStatus.EXIT); - client.close(); - executor.shutdown(); - schedule.shutdown(); - } - - private static MetricWebSocketClient initializeWebsocket(final TrafficHunterAgentProperty property, - final AgentMetadata metadata) { - return new MetricWebSocketClient( - AgentUtil.WEBSOCKET_URL.getUri(property.serverUri()), - Metadata.builder() - .agentId(metadata.agentId()) - .agentVersion(metadata.agentVersion()) - .agentName(metadata.agentName()) - .startTime(metadata.startTime()) - .status(metadata.status().get()) - .build() - ); - } - - private ThreadFactory getThreadFactory(final String threadName) { - return r -> { - Thread thread = new Thread(r); - thread.setDaemon(true); - thread.setName(threadName); - - return thread; - }; - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java deleted file mode 100644 index 2e65b3cb..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentSystemMetricSender.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.property.TrafficHunterAgentProperty; -import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; -import org.traffichunter.javaagent.jmx.collect.MetricCollectSupport; -import org.traffichunter.javaagent.jmx.metric.systeminfo.SystemInfo; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - * The {@code AgentSystemMetricSender} class is responsible for sending system metrics - * to the server via a WebSocket connection. - * - *

Features:

- *
    - *
  • Collects system metric data from the local environment.
  • - *
  • Wraps the system data with metadata and sends it in a compressed format.
  • - *
  • Uses {@link MetricCollectSupport} for metric collection.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see MetricCollectSupport - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentSystemMetricSender implements MetricSender { - - private static final Logger log = LoggerFactory.getLogger(AgentSystemMetricSender.class); - - private final MetricWebSocketClient client; - - private final MetricCollectSupport metricCollectSupport; - - public AgentSystemMetricSender(final MetricWebSocketClient client, - final TrafficHunterAgentProperty property) { - this.client = client; - this.metricCollectSupport = new MetricCollectSupport(property.targetUri()); - } - - @Override - public void toSend(final AgentMetadata metadata) { - final SystemInfo systemInfo = metricCollectSupport.collect(); - - final MetadataWrapper wrapper = MetadataWrapper.create(metadata, systemInfo); - - client.compressToSend(wrapper, MetricType.SYSTEM_METRIC); - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java b/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java deleted file mode 100644 index 1a39c5c2..00000000 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/sender/websocket/AgentTransactionMetricSender.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2024 yungwang-o - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.traffichunter.javaagent.bootstrap.engine.sender.websocket; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traffichunter.javaagent.bootstrap.engine.sender.MetricSender; -import org.traffichunter.javaagent.bootstrap.metadata.AgentMetadata; -import org.traffichunter.javaagent.bootstrap.metadata.MetadataWrapper; -import org.traffichunter.javaagent.trace.dto.TraceInfo; -import org.traffichunter.javaagent.trace.queue.TraceQueue; -import org.traffichunter.javaagent.websocket.MetricWebSocketClient; -import org.traffichunter.javaagent.websocket.converter.SerializationByteArrayConverter.MetricType; - -/** - *

- * The {@code AgentTransactionMetricSender} class is responsible for sending transaction metrics - * to the server via a WebSocket connection. - *

- * - *

Features:

- *
    - *
  • Continuously retrieves transaction data from a synchronized queue.
  • - *
  • Wraps the transaction data with metadata and sends it in a compressed format.
  • - *
  • Relies on {@link TraceQueue} for thread-safe access to transaction data.
  • - *
- * - * @see MetricSender - * @see MetricWebSocketClient - * @see TraceQueue - * - * @author yungwang-o - * @version 1.0.0 - */ -public class AgentTransactionMetricSender implements MetricSender { - - public static final Logger log = LoggerFactory.getLogger(AgentTransactionMetricSender.class); - - private final MetricWebSocketClient client; - - public AgentTransactionMetricSender(final MetricWebSocketClient client) { - this.client = client; - } - - @Override - public void toSend(final AgentMetadata metadata) { - - while (!Thread.currentThread().isInterrupted()) { - - try { - - TraceInfo trInfo = TraceQueue.INSTANCE.poll(); - - MetadataWrapper wrapper = MetadataWrapper.create(metadata, trInfo); - - client.compressToSend(wrapper, MetricType.TRANSACTION_METRIC); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } catch (IllegalStateException e) { - log.error("exception while sending transaction metric = {}", e.getMessage()); - throw new RuntimeException(e); - } - } - } -} diff --git a/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/BootstrapLoggerTest.java b/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/BootstrapLoggerTest.java new file mode 100644 index 00000000..d83513f4 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/BootstrapLoggerTest.java @@ -0,0 +1,17 @@ +package org.traffichunter.javaagent.bootstrap; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(ReplaceUnderscores.class) +class BootstrapLoggerTest { + + @Test + void bootstrap_logger_print() { + + BootstrapLogger log = BootstrapLogger.getLogger(BootstrapLogger.class); + + log.info("info {}"); + } +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/ConfigurationsTest.java b/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/ConfigurationsTest.java new file mode 100644 index 00000000..38072082 --- /dev/null +++ b/java-apm-agent/java-agent-bootstrap/src/test/java/org/traffichunter/javaagent/bootstrap/ConfigurationsTest.java @@ -0,0 +1,17 @@ +package org.traffichunter.javaagent.bootstrap; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; +import org.traffichunter.javaagent.bootstrap.Configurations.ConfigProperty; + +class ConfigurationsTest { + + @Test + void banner_mode_no_property() { + + boolean banner = Configurations.banner(ConfigProperty.BANNER_MODE); + + assertFalse(banner); + } +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-commons/build.gradle b/java-apm-agent/java-agent-commons/build.gradle deleted file mode 100644 index c2688fc2..00000000 --- a/java-apm-agent/java-agent-commons/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.commons' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-apm-agent/java-agent-commons/build.gradle.kts b/java-apm-agent/java-agent-commons/build.gradle.kts new file mode 100644 index 00000000..cd6bdf25 --- /dev/null +++ b/java-apm-agent/java-agent-commons/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("java") +} + +group = "org.traffichunter.javaagent.commons" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/type/MetricType.java b/java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/type/MetricType.java new file mode 100644 index 00000000..473c27bd --- /dev/null +++ b/java-apm-agent/java-agent-commons/src/main/java/org/traffichunter/javaagent/commons/type/MetricType.java @@ -0,0 +1,18 @@ +package org.traffichunter.javaagent.commons.type; + +public enum MetricType { + SYSTEM_METRIC((byte) 1), + TRANSACTION_METRIC((byte) 2), + LOG_METRIC((byte) 3), + ; + + private final byte value; + + MetricType(final byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-event/build.gradle b/java-apm-agent/java-agent-event/build.gradle deleted file mode 100644 index 23145d66..00000000 --- a/java-apm-agent/java-agent-event/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'java' -} - -group = 'org.traffichunter.javaagent.event' -version = '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':java-apm-agent:java-agent-commons') -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/java-apm-agent/java-agent-event/build.gradle.kts b/java-apm-agent/java-agent-event/build.gradle.kts new file mode 100644 index 00000000..6d0fddb0 --- /dev/null +++ b/java-apm-agent/java-agent-event/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("java") +} + +group = "org.traffichunter.javaagent.event" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") + + implementation(project(":java-apm-agent:java-agent-commons")) +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/java-agent-extension/build.gradle.kts b/java-apm-agent/java-agent-extension/build.gradle.kts new file mode 100644 index 00000000..b74de557 --- /dev/null +++ b/java-apm-agent/java-agent-extension/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("java") +} + +group = "org.traffichunter.javaagent.extension" +version = "0.0.1-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") + + implementation(project(":java-apm-agent:java-agent-retry")) + implementation(project(":java-apm-agent:java-agent-bootstrap")) + implementation(project(":java-apm-agent:java-agent-websocket")) + implementation(project(":java-apm-agent:java-agent-event")) + implementation(project(":java-apm-agent:java-agent-commons")) + implementation(project(":java-apm-agent:java-agent-jmx")) + + compileOnly(project(":java-apm-agent:plugin-sdk")) + + compileOnly("io.opentelemetry:opentelemetry-api:1.45.0") + implementation("io.opentelemetry:opentelemetry-sdk:1.45.0") + implementation("io.opentelemetry:opentelemetry-exporter-zipkin:1.45.0") + implementation("org.yaml:snakeyaml:2.3") + implementation("net.bytebuddy:byte-buddy:1.15.5") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AbstractPluginInstrumentation.java similarity index 64% rename from java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java rename to java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AbstractPluginInstrumentation.java index a551ade0..5e1f59c0 100644 --- a/java-apm-agent/plugin/plugin-sdk/src/main/java/org/traffichunter/javaagent/plugin/sdk/instrumentation/AbstractPluginInstrumentation.java +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AbstractPluginInstrumentation.java @@ -21,11 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.plugin.sdk.instrumentation; +package org.traffichunter.javaagent.extension; import static net.bytebuddy.matcher.ElementMatchers.any; -import net.bytebuddy.agent.builder.AgentBuilder.Transformer; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -51,18 +50,19 @@ public AbstractPluginInstrumentation(final String pluginName, this.pluginModuleVersion = pluginModuleVersion; } - public abstract Transformer transform(); + public abstract void transform(Transformer transformer); - public abstract ElementMatcher typeMatcher(); + public abstract ElementMatcher typeMatcher(); + protected ElementMatcher isMethod() { return any(); } + + // no override protected Junction classLoaderMatcher() { return any(); } public Junction ignorePackage() { return null; } - protected abstract ElementMatcher isMethod(); - public String getPluginName() { return pluginName; } @@ -74,4 +74,36 @@ public String getPluginDetailName() { public String getPluginModuleVersion() { return pluginModuleVersion; } + + public static final class Advices { + + private final ElementMatcher methodMatcher; + + private final Class adviceClass; + + private Advices(final ElementMatcher methodMatcher, final Class adviceClass) { + this.methodMatcher = methodMatcher; + this.adviceClass = adviceClass; + } + + public static Advices create(final ElementMatcher methodMatcher, final Class adviceClass) { + return new Advices(methodMatcher, adviceClass); + } + + public ElementMatcher methodMatcher() { + return methodMatcher; + } + + public Class adviceClass() { + return adviceClass; + } + + @Override + public String toString() { + return "Advice{" + + "methodMatcher=" + methodMatcher + + ", adviceClass=" + adviceClass + + '}'; + } + } } diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AgentExecutableContext.java similarity index 90% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java rename to java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AgentExecutableContext.java index 99d4b0c1..767b518a 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/AgentExecutableContext.java +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/AgentExecutableContext.java @@ -21,12 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine.context; +package org.traffichunter.javaagent.extension; -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.event.context.AgentContextStateEventHandler; +import org.traffichunter.javaagent.extension.env.ConfigurableEnvironment; /** * The {@code AgentExecutableContext} interface defines the contract for managing diff --git a/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/ConfigurableContextInitializer.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/ConfigurableContextInitializer.java new file mode 100644 index 00000000..2e059655 --- /dev/null +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/ConfigurableContextInitializer.java @@ -0,0 +1,174 @@ +/** + * The MIT License + * + * Copyright (c) 2024 yungwang-o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.extension; + +import java.io.InputStream; +import java.lang.instrument.Instrumentation; +import java.time.Instant; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.agent.builder.AgentBuilder.Identified.Extendable; +import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; +import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.DiscoveryStrategy.Reiterating; +import org.traffichunter.javaagent.bootstrap.BootstrapLogger; +import org.traffichunter.javaagent.bootstrap.Configurations; +import org.traffichunter.javaagent.bootstrap.Configurations.ConfigProperty; +import org.traffichunter.javaagent.commons.status.AgentStatus; +import org.traffichunter.javaagent.commons.util.UUIDGenerator; +import org.traffichunter.javaagent.extension.bytebuddy.AdjustTransformer; +import org.traffichunter.javaagent.extension.env.ConfigurableEnvironment; +import org.traffichunter.javaagent.extension.env.Environment; +import org.traffichunter.javaagent.extension.property.TrafficHunterAgentProperty; +import org.traffichunter.javaagent.extension.bytebuddy.AgentIgnoreMatcher; +import org.traffichunter.javaagent.extension.bytebuddy.AgentLocationStrategy; +import org.traffichunter.javaagent.extension.bytebuddy.ByteBuddyLogger.RedefinitionStrategyLoggingAdapter; +import org.traffichunter.javaagent.extension.bytebuddy.ByteBuddyLogger.TransformLoggingListenAdapter; +import org.traffichunter.javaagent.extension.loader.PluginLoader; +import org.traffichunter.javaagent.extension.loader.TrafficHunterPluginLoader; +import org.traffichunter.javaagent.extension.metadata.AgentMetadata; + +/** + * The {@code ConfigurableContextInitializer} class is responsible for initializing the environment, + * setting up agent metadata, and configuring ByteBuddy for runtime class transformations. + * + *

Features:

+ *
    + *
  • Loads properties using a configurable environment.
  • + *
  • Sets up agent metadata based on the current environment and runtime status.
  • + *
  • Configures ByteBuddy to instrument methods annotated with Spring component annotations.
  • + *
+ * + * @see ConfigurableEnvironment + * @see TrafficHunterAgentProperty + * @see ByteBuddy + * @see AgentMetadata + * + * @author yungwang-o + * @version 1.0.0 + */ +public final class ConfigurableContextInitializer { + + private static final BootstrapLogger log = BootstrapLogger.getLogger(ConfigurableContextInitializer.class); + + private static final Boolean transformLogging = Configurations.debug(ConfigProperty.TRANSFORM_DEBUG); + + private final ConfigurableEnvironment env; + + public ConfigurableContextInitializer(final ConfigurableEnvironment env) { + this.env = env; + } + + public TrafficHunterAgentProperty property() { + return env.load(); + } + + public TrafficHunterAgentProperty property(final InputStream is) { + return env.load(is); + } + + /** + * Load all plugins to manipulate the target application's bytecode. + */ + public void retransform(final Instrumentation inst) { + + List plugins = loadPlugins(TrafficHunterAgentStartAction.class.getClassLoader()); + + printLoadedPlugins(plugins); + + AgentBuilder agentBuilder = new AgentBuilder.Default() + .ignore(AgentIgnoreMatcher.ignore()) + .disableClassFormatChanges() + .with(RedefinitionStrategy.RETRANSFORMATION) + .with(Reiterating.INSTANCE) + .with(new RedefinitionStrategyLoggingAdapter()) + .with(new AgentLocationStrategy()); + + if(transformLogging) { + agentBuilder = agentBuilder.with(new TransformLoggingListenAdapter()); + } + + agentBuilder = instrument(agentBuilder, plugins); + + agentBuilder.installOn(inst); + } + + AgentBuilder instrument(AgentBuilder originalAgentBuilder, + final List pluginInstrumentation) { + + if(isEnabled(pluginInstrumentation)) { + log.warn("Instrumenting is empty!"); + return originalAgentBuilder; + } + + for(final AbstractPluginInstrumentation plugin : pluginInstrumentation) { + + Extendable transform = originalAgentBuilder + .type(plugin.typeMatcher()) + .transform(new AdjustTransformer()); + + Transformer transformer = new Transformer(transform); + + plugin.transform(transformer); + + originalAgentBuilder = transformer.agentBuilder(); + } + + return originalAgentBuilder; + } + + private boolean isEnabled(final List pluginInstrumentation) { + return pluginInstrumentation == null || pluginInstrumentation.isEmpty(); + } + + public AgentMetadata setAgentMetadata(final Instant startTime, final AgentStatus status) { + + final String agentName = property().name(); + + return new AgentMetadata( + UUIDGenerator.generate(agentName), + Environment.VERSION.version(), + agentName, + startTime, + new AtomicReference<>(status) + ); + } + + private List loadPlugins(final ClassLoader classLoader) { + + PluginLoader pluginLoader = new TrafficHunterPluginLoader(); + + return pluginLoader.loadModules(classLoader); + } + + private void printLoadedPlugins(final List plugins) { + + plugins.forEach(pluginInstrumentation -> + log.info("loaded : {}", pluginInstrumentation.getPluginDetailName()) + ); + + log.info("Total plugins : {}", plugins.size()); + } +} diff --git a/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/LogRecord.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/LogRecord.java new file mode 100644 index 00000000..17e34d68 --- /dev/null +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/LogRecord.java @@ -0,0 +1,159 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.extension; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.resources.Resource; +import java.util.Map; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +public class LogRecord { + + private final Map resource; + + private final Map instrumentationScopeInfo; + + private final Map attributes; + + private final String body; + + private final int totalAttributeCount; + + private final Severity severity; + + private final String severityText; + + private final long observedTimestampEpochNanos; + + private final long timestampEpochNanos; + + private LogRecord(Builder builder) { + this.resource = builder.resource; + this.instrumentationScopeInfo = builder.instrumentationScopeInfo; + this.attributes = builder.attributes; + this.body = builder.body; + this.totalAttributeCount = builder.totalAttributeCount; + this.severity = builder.severity; + this.severityText = builder.severityText; + this.observedTimestampEpochNanos = builder.observedTimestampEpochNanos; + this.timestampEpochNanos = builder.timestampEpochNanos; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Map resource; + + private Map instrumentationScopeInfo; + + private Map attributes; + + private String body; + + private int totalAttributeCount; + + private Severity severity; + + private String severityText; + + private long observedTimestampEpochNanos; + + private long timestampEpochNanos; + + public Builder resource(Resource resource) { + this.resource = OpenTelemetryParser.doParse(resource); + return this; + } + + public Builder instrumentationScopeInfo(InstrumentationScopeInfo info) { + this.instrumentationScopeInfo = OpenTelemetryParser.doParse(info); + return this; + } + + public Builder attributes(Attributes attributes) { + this.attributes = OpenTelemetryParser.doParse(attributes); + return this; + } + + public Builder body(String body) { + this.body = body; + return this; + } + + public Builder totalAttributeCount(int count) { + this.totalAttributeCount = count; + return this; + } + + public Builder severity(Severity severity) { + this.severity = severity; + return this; + } + + public Builder severityText(String text) { + this.severityText = text; + return this; + } + + public Builder observedTimestampEpochNanos(long ts) { + this.observedTimestampEpochNanos = ts; + return this; + } + + public LogRecord.Builder timestampEpochNanos(long ts) { + this.timestampEpochNanos = ts; + return this; + } + + public LogRecord build() { + return new LogRecord(this); + } + } + + public Map getResource() { return resource; } + + public Map getInstrumentationScopeInfo() { return instrumentationScopeInfo; } + + public Map getAttributes() { return attributes; } + + public String getBody() { return body; } + + public int getTotalAttributeCount() { return totalAttributeCount; } + + public Severity getSeverity() { return severity; } + + public String getSeverityText() { return severityText; } + + public long getObservedTimestampEpochNanos() { return observedTimestampEpochNanos; } + + public long getTimestampEpochNanos() { return timestampEpochNanos; } +} diff --git a/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryManager.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryManager.java new file mode 100644 index 00000000..9800bbc8 --- /dev/null +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryManager.java @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.extension; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.logs.LogLimits; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.IdGenerator; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import org.traffichunter.javaagent.bootstrap.Configurations; +import org.traffichunter.javaagent.bootstrap.Configurations.ConfigProperty; +import org.traffichunter.javaagent.extension.exporter.thunter.TrafficHunterLogExporter; +import org.traffichunter.javaagent.extension.exporter.thunter.TrafficHunterSpanExporter; +import org.traffichunter.javaagent.extension.exporter.zipkin.ZipkinSpanExportDelegator; +import org.traffichunter.javaagent.extension.metadata.AgentMetadata; +import org.traffichunter.javaagent.websocket.TrafficHunterWebsocketClient; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +final class OpenTelemetryManager { + + private static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); + + private static final String endpoint = Configurations.export(ConfigProperty.ZIPKIN_EXPORTER_ENDPOINT); + + private OpenTelemetryManager() {} + + static OpenTelemetrySdk manageOpenTelemetrySdk(final String serviceName, + final TrafficHunterWebsocketClient client, + final AgentMetadata metadata) { + + SdkTracerProvider tracerProvider = createSdkTracerProvider(serviceName, client, metadata); + + SdkLoggerProvider loggerProvider = createSdkLoggerProvider(serviceName, client, metadata); + + return OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setLoggerProvider(loggerProvider) + .buildAndRegisterGlobal(); + } + + private static SdkTracerProvider createSdkTracerProvider(final String serviceName, + final TrafficHunterWebsocketClient client, + final AgentMetadata metadata) { + + SdkTracerProviderBuilder sdkTracerProviderBuilder = SdkTracerProvider.builder(); + + if(!(endpoint == null || endpoint.isEmpty())) { + sdkTracerProviderBuilder.addSpanProcessor( + SimpleSpanProcessor.create(new ZipkinSpanExportDelegator(endpoint)) + ); + } + + return sdkTracerProviderBuilder + .addSpanProcessor(SimpleSpanProcessor.create(new TrafficHunterSpanExporter(client, metadata))) + .setIdGenerator(IdGenerator.random()) + .setSampler(Sampler.alwaysOn()) + .setResource(Resource.create(Attributes.of(SERVICE_NAME, serviceName))) + .build(); + } + + private static SdkLoggerProvider createSdkLoggerProvider(final String serviceName, + final TrafficHunterWebsocketClient client, + final AgentMetadata metadata) { + + return SdkLoggerProvider.builder() + .addLogRecordProcessor(SimpleLogRecordProcessor.create(new TrafficHunterLogExporter(client, metadata))) + .setLogLimits(LogLimits::getDefault) + .setResource(Resource.create(Attributes.of(SERVICE_NAME, serviceName))) + .build(); + } +} diff --git a/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryParser.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryParser.java new file mode 100644 index 00000000..3660df8d --- /dev/null +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/OpenTelemetryParser.java @@ -0,0 +1,47 @@ +package org.traffichunter.javaagent.extension; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.resources.Resource; +import java.util.HashMap; +import java.util.Map; + +final class OpenTelemetryParser { + + public static Map doParse(final Attributes attributes) { + Map attributesMap = new HashMap<>(); + + Map, Object> asMap = attributes.asMap(); + + for (Map.Entry, Object> entry : asMap.entrySet()) { + attributesMap.put(entry.getKey().toString(), entry.getValue().toString()); + } + + return attributesMap; + } + + public static Map doParse(final Resource resource) { + Map resourceMap = new HashMap<>(); + + Map, Object> asMap = resource.getAttributes().asMap(); + + for (Map.Entry, Object> entry : asMap.entrySet()) { + resourceMap.put(entry.getKey().toString(), entry.getValue().toString()); + } + + return resourceMap; + } + + public static Map doParse(final InstrumentationScopeInfo instrumentationScopeInfo) { + Map instrumentationScopeInfoMap = new HashMap<>(); + + Map, Object> asMap = instrumentationScopeInfo.getAttributes().asMap(); + + for (Map.Entry, Object> entry : asMap.entrySet()) { + instrumentationScopeInfoMap.put(entry.getKey().toString(), entry.getValue().toString()); + } + + return instrumentationScopeInfoMap; + } +} diff --git a/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/PluginToolClassLoader.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/PluginToolClassLoader.java new file mode 100644 index 00000000..1f011773 --- /dev/null +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/PluginToolClassLoader.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright (c) 2024 traffic-hunter.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.traffichunter.javaagent.extension; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +/** + * @author yungwang-o + * @version 1.1.0 + */ +public class PluginToolClassLoader extends URLClassLoader { + + private static final String JAVA_PREFIX_JAR_ENTRY = "extension/"; + + static { + ClassLoader.registerAsParallelCapable(); + } + + private PluginToolClassLoader(final URL url, final ClassLoader parent) { + super(new URL[]{url}, parent); + } + + private static URLClassLoader create(final ClassLoader parent, final URL url) { + return new PluginToolClassLoader(url, parent); + } + + public static ClassLoader generateClassLoader(final ClassLoader parent, final File agentFIle) { + + if(parent == null) { + throw new IllegalStateException("targetClassLoader is not bootCL"); + } + + List urlClassLoaders = new ArrayList<>(); + + + return null; + } + + private static List parseEntryUrl(final File agnetfile) throws IOException { + + try (JarFile jarFile = new JarFile(agnetfile)){ + + return jarFile.stream() + .map(ZipEntry::getName) + .filter(name -> extractClass(replace(name))) + .toList(); + } + } + + private static String replace(final String jarEntryName) { + return jarEntryName.replace('.', '/') + ".class"; + } + + private static boolean extractClass(final String getName) { + + return (getName.contains("org/traffichunter/javaagent/plugin") || + getName.contains(JAVA_PREFIX_JAR_ENTRY + "otel")) && + getName.startsWith(JAVA_PREFIX_JAR_ENTRY) && + getName.endsWith(".class"); + } + + @Override + public URL getResource(final String name) { + return super.getResource(name); + } +} diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceInfo.java similarity index 90% rename from java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java rename to java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceInfo.java index c180b4dd..400516b5 100644 --- a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/dto/TraceInfo.java +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceInfo.java @@ -21,12 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.trace.dto; +package org.traffichunter.javaagent.extension; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.sdk.trace.data.SpanData; import java.time.Duration; import java.time.Instant; +import java.util.Map; import java.util.Objects; /** @@ -43,6 +44,10 @@ public record TraceInfo( String spanId, + Map attributes, + + int attributesCount, + Instant startTime, Instant endTime, @@ -54,6 +59,7 @@ public record TraceInfo( boolean ended ) { + public static TraceInfo translate(final SpanData spanData) { Instant startTime = Instant.EPOCH.plusNanos(spanData.getStartEpochNanos()); @@ -62,11 +68,15 @@ public static TraceInfo translate(final SpanData spanData) { Duration between = Duration.between(startTime, endTime); + Map map = OpenTelemetryParser.doParse(spanData.getAttributes()); + return new TraceInfo( spanData.getName(), spanData.getTraceId(), spanData.getParentSpanId(), spanData.getSpanId(), + map, + map.size(), startTime, endTime, between.toMillis(), diff --git a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceQueue.java similarity index 94% rename from java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java rename to java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceQueue.java index 3dbbf577..be115f7e 100644 --- a/java-apm-agent/java-agent-trace/src/main/java/org/traffichunter/javaagent/trace/queue/TraceQueue.java +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TraceQueue.java @@ -21,16 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.trace.queue; +package org.traffichunter.javaagent.extension; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import org.traffichunter.javaagent.trace.dto.TraceInfo; /** * @author yungwang-o * @version 1.0.0 */ +@Deprecated public enum TraceQueue { INSTANCE, diff --git a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TrafficHunterAgentExecutableContext.java similarity index 80% rename from java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java rename to java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TrafficHunterAgentExecutableContext.java index 5d523642..c956235f 100644 --- a/java-apm-agent/java-agent-bootstrap/src/main/java/org/traffichunter/javaagent/bootstrap/engine/context/execute/TrafficHunterAgentExecutableContext.java +++ b/java-apm-agent/java-agent-extension/src/main/java/org/traffichunter/javaagent/extension/TrafficHunterAgentExecutableContext.java @@ -21,24 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.traffichunter.javaagent.bootstrap.engine.context.execute; +package org.traffichunter.javaagent.extension; +import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; -import org.traffichunter.javaagent.bootstrap.engine.TrafficHunterAgentShutdownHook; -import org.traffichunter.javaagent.bootstrap.engine.context.AgentExecutableContext; -import org.traffichunter.javaagent.bootstrap.engine.context.configuration.ConfigurableContextInitializer; -import org.traffichunter.javaagent.bootstrap.engine.env.ConfigurableEnvironment; +import java.util.logging.Logger; +import org.traffichunter.javaagent.bootstrap.TrafficHunterAgentShutdownHook; import org.traffichunter.javaagent.commons.status.AgentStatus; import org.traffichunter.javaagent.event.listener.AgentStateEventListener; import org.traffichunter.javaagent.event.object.AgentStateEvent; import org.traffichunter.javaagent.event.store.AgentStateEventStore; +import org.traffichunter.javaagent.extension.env.ConfigurableEnvironment; /** + *

* The {@code TrafficHunterAgentExecutableContext} class represents the execution context * for the TrafficHunter Agent. It manages the agent's state, event listeners, environment * configuration, and shutdown operations. + *

* *

Purpose:

*