From a0faa735798a71fe75c7d06c35654030604d257d Mon Sep 17 00:00:00 2001 From: hexiaofeng Date: Thu, 16 Jan 2025 16:00:03 +0800 Subject: [PATCH 1/3] Fixed the issue that the WAR application cannot automatically register within the Tomcat container --- .../src/main/resources/application.properties | 1 - .../bytebuddy/handler/LoggerHandler.java | 22 +++- .../joylive-application-springboot2/pom.xml | 7 ++ .../v2/context/SpringAppContext.java | 4 + .../NacosRegistrationDefinition.java | 47 ++++++++ ...> SpringApplicationContextDefinition.java} | 12 +- ...ringApplicationRunListenersDefinition.java | 4 +- ...cationEnvironmentPreparedInterceptor.java} | 10 +- .../ApplicationLoadInterceptor.java | 6 +- .../ApplicationReadyInterceptor.java | 5 +- .../ApplicationStartedInterceptor.java | 5 +- ...r.java => ApplicationStopInterceptor.java} | 9 +- .../NacosRegistrationInterceptor.java | 111 ++++++++++++++++++ .../springboot/v2/listener/InnerListener.java | 67 +++++++++++ ...nt.core.plugin.definition.PluginDefinition | 3 +- 15 files changed, 289 insertions(+), 24 deletions(-) create mode 100644 joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/NacosRegistrationDefinition.java rename joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/{ApplicationContextDefinition.java => SpringApplicationContextDefinition.java} (82%) rename joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/{EnvironmentPreparedInterceptor.java => ApplicationEnvironmentPreparedInterceptor.java} (71%) rename joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/{ContextStopInterceptor.java => ApplicationStopInterceptor.java} (74%) create mode 100644 joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java create mode 100644 joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/listener/InnerListener.java diff --git a/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/src/main/resources/application.properties b/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/src/main/resources/application.properties index 04cbc6d99..53d1bb96a 100755 --- a/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/src/main/resources/application.properties +++ b/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/src/main/resources/application.properties @@ -1,4 +1,3 @@ -server.port=${SERVER_PORT:${random.int[11000,11999]}} spring.application.name=service-provider spring.cloud.nacos.discovery.server-addr=${NACOS_ADDR:${AP_SD_INTERNAL_ADDRESS}:${AP_SD_INTERNAL_HTTP_PORT}} spring.cloud.nacos.discovery.enabled=true diff --git a/joylive-implement/joylive-bytekit/joylive-bytekit-bytebuddy/src/main/java/com/jd/live/agent/implement/bytekit/bytebuddy/handler/LoggerHandler.java b/joylive-implement/joylive-bytekit/joylive-bytekit-bytebuddy/src/main/java/com/jd/live/agent/implement/bytekit/bytebuddy/handler/LoggerHandler.java index eac92b7ec..82c3384d4 100644 --- a/joylive-implement/joylive-bytekit/joylive-bytekit-bytebuddy/src/main/java/com/jd/live/agent/implement/bytekit/bytebuddy/handler/LoggerHandler.java +++ b/joylive-implement/joylive-bytekit/joylive-bytekit-bytebuddy/src/main/java/com/jd/live/agent/implement/bytekit/bytebuddy/handler/LoggerHandler.java @@ -61,7 +61,8 @@ public void onDiscovery(@NeverNull String typeName, @MaybeNull JavaModule module, boolean loaded) { if (logger.isDebugEnabled()) { - String message = String.format("ByteKit discovery %s [%s, %s, %s, loaded=%b]", typeName, classLoader, module, Thread.currentThread(), loaded); + String message = String.format("ByteKit discovery %s [%s, %s, %s, loaded=%b]", + typeName, getName(classLoader), module, Thread.currentThread(), loaded); logger.debug(message); } } @@ -73,7 +74,8 @@ public void onTransformation(@NeverNull TypeDescription typeDescription, boolean loaded, @NeverNull DynamicType dynamicType) { if (logger.isInfoEnabled()) { - String message = String.format("ByteKit transform %s [%s, %s, %s, loaded=%b]", typeDescription.getName(), classLoader, module, Thread.currentThread(), loaded); + String message = String.format("ByteKit transform %s [%s, %s, %s, loaded=%b]", + typeDescription.getName(), getName(classLoader), module, Thread.currentThread(), loaded); logger.info(message); } } @@ -84,7 +86,8 @@ public void onIgnored(@NeverNull TypeDescription typeDescription, @MaybeNull JavaModule module, boolean loaded) { if (logger.isDebugEnabled()) { - String message = String.format("ByteKit ignore %s [%s, %s, %s, loaded=%b]", typeDescription.getName(), classLoader, module, Thread.currentThread(), loaded); + String message = String.format("ByteKit ignore %s [%s, %s, %s, loaded=%b]", + typeDescription.getName(), getName(classLoader), module, Thread.currentThread(), loaded); logger.debug(message); } } @@ -97,7 +100,8 @@ public void onError(@NeverNull String typeName, @NeverNull Throwable throwable) { OutputStream bos = new ByteArrayOutputStream(1024); PrintStream printStream = new PrintStream(bos); - printStream.printf("ByteKit error %s [%s, %s, %s, loaded=%b]", typeName, classLoader, module, Thread.currentThread(), loaded); + printStream.printf("ByteKit error %s [%s, %s, %s, loaded=%b]", + typeName, getName(classLoader), module, Thread.currentThread(), loaded); throwable.printStackTrace(printStream); logger.error(bos.toString()); } @@ -108,9 +112,17 @@ public void onComplete(@NeverNull String typeName, @MaybeNull JavaModule module, boolean loaded) { if (logger.isDebugEnabled()) { - String message = String.format("ByteKit complete %s [%s, %s, %s, loaded=%b]", typeName, classLoader, module, Thread.currentThread(), loaded); + String message = String.format("ByteKit complete %s [%s, %s, %s, loaded=%b]", + typeName, getName(classLoader), module, Thread.currentThread(), loaded); logger.debug(message); } } + + protected String getName(ClassLoader classLoader) { + if (classLoader == null) { + return ""; + } + return classLoader.getClass().getSimpleName(); + } } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/pom.xml b/joylive-plugin/joylive-application/joylive-application-springboot2/pom.xml index b9887ef69..65bc4e474 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/pom.xml +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/pom.xml @@ -15,6 +15,7 @@ 5.3.39 3.4.0 3.1.9 + 2023.0.3.2 @@ -37,6 +38,12 @@ ${spring-cloud-context.version} provided + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${nacos-discovery.version} + provided + \ No newline at end of file diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/context/SpringAppContext.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/context/SpringAppContext.java index b4012dcf0..36aa23cd5 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/context/SpringAppContext.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/context/SpringAppContext.java @@ -80,6 +80,10 @@ public void subscribe(ConfigCenter configCenter) { } } + public ConfigurableApplicationContext getContext() { + return context; + } + /** * Adds an environment listener to the given Configurator instance. * diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/NacosRegistrationDefinition.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/NacosRegistrationDefinition.java new file mode 100644 index 000000000..48fa26ebc --- /dev/null +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/NacosRegistrationDefinition.java @@ -0,0 +1,47 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.plugin.application.springboot.v2.definition; + +import com.jd.live.agent.core.bytekit.matcher.MatcherBuilder; +import com.jd.live.agent.core.extension.annotation.ConditionalOnClass; +import com.jd.live.agent.core.extension.annotation.Extension; +import com.jd.live.agent.core.inject.annotation.Injectable; +import com.jd.live.agent.core.plugin.definition.InterceptorDefinition; +import com.jd.live.agent.core.plugin.definition.InterceptorDefinitionAdapter; +import com.jd.live.agent.core.plugin.definition.PluginDefinition; +import com.jd.live.agent.core.plugin.definition.PluginDefinitionAdapter; +import com.jd.live.agent.governance.annotation.ConditionalOnGovernanceEnabled; +import com.jd.live.agent.plugin.application.springboot.v2.interceptor.NacosRegistrationInterceptor; + +/** + * NacosRegistrationDefinition + */ +@Injectable +@Extension(value = "NacosRegistrationDefinition", order = PluginDefinition.ORDER_REGISTRY) +@ConditionalOnGovernanceEnabled +@ConditionalOnClass(NacosRegistrationDefinition.TYPE_NACOS_AUTO_SERVICE_REGISTRATION) +public class NacosRegistrationDefinition extends PluginDefinitionAdapter { + + protected static final String TYPE_NACOS_AUTO_SERVICE_REGISTRATION = "com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration"; + + public NacosRegistrationDefinition() { + this.matcher = () -> MatcherBuilder.named(TYPE_NACOS_AUTO_SERVICE_REGISTRATION); + this.interceptors = new InterceptorDefinition[]{ + new InterceptorDefinitionAdapter( + MatcherBuilder.isConstructor(), NacosRegistrationInterceptor::new), + }; + } +} diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/ApplicationContextDefinition.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationContextDefinition.java similarity index 82% rename from joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/ApplicationContextDefinition.java rename to joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationContextDefinition.java index 5d657ee1d..5a3f8bc93 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/ApplicationContextDefinition.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationContextDefinition.java @@ -26,13 +26,13 @@ import com.jd.live.agent.core.plugin.definition.PluginDefinition; import com.jd.live.agent.core.plugin.definition.PluginDefinitionAdapter; import com.jd.live.agent.governance.annotation.ConditionalOnGovernanceEnabled; -import com.jd.live.agent.plugin.application.springboot.v2.interceptor.ContextStopInterceptor; +import com.jd.live.agent.plugin.application.springboot.v2.interceptor.ApplicationStopInterceptor; @Injectable -@Extension(value = "ApplicationContextDefinition_v5", order = PluginDefinition.ORDER_APPLICATION) +@Extension(value = "SpringApplicationContextDefinition_v5", order = PluginDefinition.ORDER_APPLICATION) @ConditionalOnGovernanceEnabled -@ConditionalOnClass(ApplicationContextDefinition.TYPE_ABSTRACT_APPLICATION_CONTEXT) -public class ApplicationContextDefinition extends PluginDefinitionAdapter { +@ConditionalOnClass(SpringApplicationContextDefinition.TYPE_ABSTRACT_APPLICATION_CONTEXT) +public class SpringApplicationContextDefinition extends PluginDefinitionAdapter { protected static final String TYPE_ABSTRACT_APPLICATION_CONTEXT = "org.springframework.context.support.AbstractApplicationContext"; @@ -41,10 +41,10 @@ public class ApplicationContextDefinition extends PluginDefinitionAdapter { @Inject(value = AppListener.COMPONENT_APPLICATION_LISTENER, component = true) private AppListener listener; - public ApplicationContextDefinition() { + public SpringApplicationContextDefinition() { this.matcher = () -> MatcherBuilder.named(TYPE_ABSTRACT_APPLICATION_CONTEXT); this.interceptors = new InterceptorDefinition[]{ - new InterceptorDefinitionAdapter(MatcherBuilder.named(METHOD_STOP), () -> new ContextStopInterceptor(listener)) + new InterceptorDefinitionAdapter(MatcherBuilder.named(METHOD_STOP), () -> new ApplicationStopInterceptor(listener)) }; } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationRunListenersDefinition.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationRunListenersDefinition.java index d2547523c..dfb87dc2f 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationRunListenersDefinition.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/definition/SpringApplicationRunListenersDefinition.java @@ -29,7 +29,7 @@ import com.jd.live.agent.governance.annotation.ConditionalOnGovernanceEnabled; import com.jd.live.agent.plugin.application.springboot.v2.interceptor.ApplicationReadyInterceptor; import com.jd.live.agent.plugin.application.springboot.v2.interceptor.ApplicationStartedInterceptor; -import com.jd.live.agent.plugin.application.springboot.v2.interceptor.EnvironmentPreparedInterceptor; +import com.jd.live.agent.plugin.application.springboot.v2.interceptor.ApplicationEnvironmentPreparedInterceptor; @Injectable @Extension(value = "SpringApplicationRunListenersDefinition_v5", order = PluginDefinition.ORDER_APPLICATION) @@ -63,7 +63,7 @@ public SpringApplicationRunListenersDefinition() { new InterceptorDefinitionAdapter(MatcherBuilder.in(METHOD_READY, METHOD_RUNNING), () -> new ApplicationReadyInterceptor(listener)), new InterceptorDefinitionAdapter(MatcherBuilder.in(METHOD_ENVIRONMENT_PREPARED), - () -> new EnvironmentPreparedInterceptor(listener)) + () -> new ApplicationEnvironmentPreparedInterceptor(listener)) }; } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/EnvironmentPreparedInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationEnvironmentPreparedInterceptor.java similarity index 71% rename from joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/EnvironmentPreparedInterceptor.java rename to joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationEnvironmentPreparedInterceptor.java index d91e22957..ef261b997 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/EnvironmentPreparedInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationEnvironmentPreparedInterceptor.java @@ -20,6 +20,7 @@ import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppBootstrapContext; import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppEnvironment; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; /** * An interceptor that adds a Configurator-based PropertySource to the ConfigurableEnvironment @@ -27,16 +28,19 @@ * * @since 1.6.0 */ -public class EnvironmentPreparedInterceptor extends InterceptorAdaptor { +public class ApplicationEnvironmentPreparedInterceptor extends InterceptorAdaptor { private final AppListener listener; - public EnvironmentPreparedInterceptor(AppListener listener) { + public ApplicationEnvironmentPreparedInterceptor(AppListener listener) { this.listener = listener; } @Override public void onEnter(ExecutableContext ctx) { - listener.onEnvironmentPrepared(new SpringAppBootstrapContext(), new SpringAppEnvironment(ctx.getArgument(1))); + SpringAppBootstrapContext context = new SpringAppBootstrapContext(); + SpringAppEnvironment environment = new SpringAppEnvironment(ctx.getArgument(1)); + InnerListener.foreach(l -> l.onEnvironmentPrepared(context, environment)); + listener.onEnvironmentPrepared(context, environment); } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationLoadInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationLoadInterceptor.java index 5bbc4398c..e01d6bf1e 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationLoadInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationLoadInterceptor.java @@ -19,6 +19,7 @@ import com.jd.live.agent.bootstrap.bytekit.context.MethodContext; import com.jd.live.agent.core.bootstrap.AppListener; import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; import org.springframework.boot.SpringApplication; public class ApplicationLoadInterceptor extends InterceptorAdaptor { @@ -33,6 +34,9 @@ public ApplicationLoadInterceptor(AppListener listener) { public void onSuccess(ExecutableContext ctx) { MethodContext mc = (MethodContext) ctx; SpringApplication application = (SpringApplication) mc.getTarget(); - listener.onLoading(application.getClassLoader(), mc.getResult()); + ClassLoader classLoader = application.getClassLoader(); + Class mainClass = mc.getResult(); + InnerListener.foreach(l -> l.onLoading(classLoader, mainClass)); + listener.onLoading(classLoader, mainClass); } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationReadyInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationReadyInterceptor.java index 7a2baa6d0..65bc726f0 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationReadyInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationReadyInterceptor.java @@ -19,6 +19,7 @@ import com.jd.live.agent.core.bootstrap.AppListener; import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppContext; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; public class ApplicationReadyInterceptor extends InterceptorAdaptor { @@ -30,6 +31,8 @@ public ApplicationReadyInterceptor(AppListener listener) { @Override public void onEnter(ExecutableContext ctx) { - listener.onReady(new SpringAppContext(ctx.getArgument(0))); + SpringAppContext context = new SpringAppContext(ctx.getArgument(0)); + InnerListener.foreach(l -> l.onReady(context)); + listener.onReady(context); } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStartedInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStartedInterceptor.java index df5aa6969..3e4f635cd 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStartedInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStartedInterceptor.java @@ -19,6 +19,7 @@ import com.jd.live.agent.core.bootstrap.AppListener; import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppContext; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; public class ApplicationStartedInterceptor extends InterceptorAdaptor { @@ -30,6 +31,8 @@ public ApplicationStartedInterceptor(AppListener listener) { @Override public void onEnter(ExecutableContext ctx) { - listener.onStarted(new SpringAppContext(ctx.getArgument(0))); + SpringAppContext context = new SpringAppContext(ctx.getArgument(0)); + InnerListener.foreach(l -> l.onStarted(context)); + listener.onStarted(context); } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ContextStopInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStopInterceptor.java similarity index 74% rename from joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ContextStopInterceptor.java rename to joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStopInterceptor.java index 0767e432e..17d618149 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ContextStopInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/ApplicationStopInterceptor.java @@ -19,18 +19,21 @@ import com.jd.live.agent.core.bootstrap.AppListener; import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppContext; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; import org.springframework.context.ConfigurableApplicationContext; -public class ContextStopInterceptor extends InterceptorAdaptor { +public class ApplicationStopInterceptor extends InterceptorAdaptor { private final AppListener listener; - public ContextStopInterceptor(AppListener listener) { + public ApplicationStopInterceptor(AppListener listener) { this.listener = listener; } @Override public void onEnter(ExecutableContext ctx) { - listener.onStop(new SpringAppContext((ConfigurableApplicationContext) ctx.getTarget())); + SpringAppContext context = new SpringAppContext((ConfigurableApplicationContext) ctx.getTarget()); + InnerListener.foreach(l -> l.onStop(context)); + listener.onStop(context); } } diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java new file mode 100644 index 000000000..4aeb0ab4b --- /dev/null +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java @@ -0,0 +1,111 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.plugin.application.springboot.v2.interceptor; + +import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration; +import com.jd.live.agent.bootstrap.bytekit.context.ExecutableContext; +import com.jd.live.agent.bootstrap.logger.Logger; +import com.jd.live.agent.bootstrap.logger.LoggerFactory; +import com.jd.live.agent.core.bootstrap.AppContext; +import com.jd.live.agent.core.bootstrap.AppListener.AppListenerAdapter; +import com.jd.live.agent.core.plugin.definition.InterceptorAdaptor; +import com.jd.live.agent.plugin.application.springboot.v2.context.SpringAppContext; +import com.jd.live.agent.plugin.application.springboot.v2.listener.InnerListener; +import org.springframework.context.ConfigurableApplicationContext; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.Query; +import java.lang.management.ManagementFactory; +import java.util.Set; + +/** + * NacosRegistrationInterceptor + */ +public class NacosRegistrationInterceptor extends InterceptorAdaptor { + + private static final Logger logger = LoggerFactory.getLogger(NacosRegistrationInterceptor.class); + + @Override + public void onSuccess(ExecutableContext ctx) { + // Fixed the issue that the WAR application cannot automatically register within the Tomcat container. + NacosAutoServiceRegistration registration = (NacosAutoServiceRegistration) ctx.getTarget(); + InnerListener.add(new AppListenerAdapter() { + @Override + public void onStarted(AppContext appContext) { + ConfigurableApplicationContext context = ((SpringAppContext) appContext).getContext(); + if (registration.isAutoStartup() && !registration.isRunning()) { + PortDetector detector = PortDetectorFactory.get(context); + try { + Integer port = detector.getPort(); + if (port != null) { + registration.setPort(port); + } + registration.start(); + } catch (Throwable e) { + logger.error("Failed to start nacos registration, caused by " + e.getMessage(), e); + } + } + InnerListener.remove(this); + } + }); + } + + /** + * An interface that defines a method to get the port number. + */ + private interface PortDetector { + /** + * Returns the port number. + * + * @return The port number as an Integer, or null if the port cannot be determined. + * @throws Throwable If an error occurs while trying to determine the port. + */ + Integer getPort() throws Throwable; + } + + /** + * A class that implements the PortDetector interface by using JMX to determine the port. + */ + private static class JmxPortDetector implements PortDetector { + + @Override + public Integer getPort() throws Throwable { + MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer(); + Set objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"))); + String value = objectNames.isEmpty() ? null : objectNames.iterator().next().getKeyProperty("port"); + return value == null ? null : Integer.valueOf(value); + } + } + + /** + * A factory class that provides a PortDetector instance based on the given ConfigurableApplicationContext. + */ + private static class PortDetectorFactory { + + /** + * Returns a PortDetector instance based on the given ConfigurableApplicationContext. + * + * @param context The ConfigurableApplicationContext. + * @return A PortDetector instance. + */ + public static PortDetector get(ConfigurableApplicationContext context) { + return new JmxPortDetector(); + } + + } + +} diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/listener/InnerListener.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/listener/InnerListener.java new file mode 100644 index 000000000..741f8b3d7 --- /dev/null +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/listener/InnerListener.java @@ -0,0 +1,67 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.plugin.application.springboot.v2.listener; + +import com.jd.live.agent.core.bootstrap.AppListener; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; + +/** + * A utility class for managing a list of AppListeners. + */ +public class InnerListener { + + /** + * A thread-safe list of AppListeners. + */ + private final static List listeners = new CopyOnWriteArrayList<>(); + + /** + * Adds an AppListener to the list of listeners. + * + * @param listener The AppListener to be added. + */ + public static void add(AppListener listener) { + if (listener != null) { + listeners.add(listener); + } + } + + /** + * Removes an AppListener from the list of listeners. + * + * @param listener The AppListener to be removed. + */ + public static void remove(AppListener listener) { + if (listener != null) { + listeners.remove(listener); + } + } + + /** + * Iterates over the list of AppListeners and performs the given action on each listener. + * + * @param consumer The action to be performed on each AppListener. + */ + public static void foreach(Consumer consumer) { + if (consumer != null) { + listeners.forEach(consumer); + } + } +} + diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition index 50e95782c..cd0ab743d 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/resources/META-INF/services/com.jd.live.agent.core.plugin.definition.PluginDefinition @@ -1,3 +1,4 @@ com.jd.live.agent.plugin.application.springboot.v2.definition.SpringApplicationDefinition com.jd.live.agent.plugin.application.springboot.v2.definition.SpringApplicationRunListenersDefinition -com.jd.live.agent.plugin.application.springboot.v2.definition.ApplicationContextDefinition +com.jd.live.agent.plugin.application.springboot.v2.definition.SpringApplicationContextDefinition +com.jd.live.agent.plugin.application.springboot.v2.definition.NacosRegistrationDefinition \ No newline at end of file From 3eb05208e6421df2a90ccbd7f0fc733091275d35 Mon Sep 17 00:00:00 2001 From: hexiaofeng Date: Thu, 16 Jan 2025 17:23:29 +0800 Subject: [PATCH 2/3] Find boot resource in tomcat --- .../bootstrap/env/AbstractEnvSupplier.java | 42 ++++---- .../bootstrap/resource/BootResourcer.java | 40 +++++++ .../loader/ClassloaderBootResourcer.java | 43 ++++++++ .../resource/tomcat/TomcatBootResourcer.java | 102 ++++++++++++++++++ ...gent.core.bootstrap.resource.BootResourcer | 2 + .../pom.xml | 2 +- 6 files changed, 212 insertions(+), 19 deletions(-) create mode 100644 joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/BootResourcer.java create mode 100644 joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/loader/ClassloaderBootResourcer.java create mode 100644 joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/tomcat/TomcatBootResourcer.java create mode 100644 joylive-core/joylive-core-framework/src/main/resources/META-INF/services/com.jd.live.agent.core.bootstrap.resource.BootResourcer diff --git a/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/env/AbstractEnvSupplier.java b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/env/AbstractEnvSupplier.java index cf1a40a33..11b7bceb2 100644 --- a/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/env/AbstractEnvSupplier.java +++ b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/env/AbstractEnvSupplier.java @@ -19,16 +19,15 @@ import com.jd.live.agent.bootstrap.logger.LoggerFactory; import com.jd.live.agent.core.bootstrap.EnvSupplier; import com.jd.live.agent.core.bootstrap.env.config.ConfigEnvSupplier; +import com.jd.live.agent.core.bootstrap.resource.BootResourcer; import com.jd.live.agent.core.inject.annotation.Inject; import com.jd.live.agent.core.parser.ConfigParser; +import com.jd.live.agent.core.util.Close; import com.jd.live.agent.core.util.StringUtils; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; +import java.io.*; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -46,6 +45,9 @@ public abstract class AbstractEnvSupplier implements EnvSupplier { @Inject private Map parsers; + @Inject + private List resourcers; + /** * Constructs an AbstractEnvSupplier with the specified resources. * @@ -89,31 +91,35 @@ protected Map loadConfigs(String resource, String prefix) { String ext = pos > 0 ? resource.substring(pos + 1) : ""; ConfigParser parser = parsers.get(ext); if (parser != null) { - URL url = getResource(resource); - if (url != null) { + InputStream stream = getResource(resource); + if (stream != null) { try { - Map result = parse(url.openStream(), parser); - logger.info("[LiveAgent] Successfully load config from " + url); + Map result = parse(stream, parser); + logger.info("[LiveAgent] Successfully load config from " + resource); return result; } catch (Throwable e) { - logger.warn("[LiveAgent] Failed to load config from " + url, e); + logger.warn("[LiveAgent] Failed to load config from " + resource, e); return new HashMap<>(); + } finally { + Close.instance().close(stream); } } } return null; } - protected URL getResource(String resource) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - URL url = contextClassLoader.getResource(resource); - if (url == null) { - ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - if (systemClassLoader != contextClassLoader) { - url = systemClassLoader.getResource(resource); + protected InputStream getResource(String resource) { + for (BootResourcer resourcer : resourcers) { + try { + InputStream stream = resourcer.getResource(resource); + if (stream != null) { + return stream; + } + } catch (IOException e) { + return null; } } - return url; + return null; } /** diff --git a/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/BootResourcer.java b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/BootResourcer.java new file mode 100644 index 000000000..abfd3794e --- /dev/null +++ b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/BootResourcer.java @@ -0,0 +1,40 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.core.bootstrap.resource; + +import com.jd.live.agent.core.extension.annotation.Extensible; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An interface for finding resources. + */ +@Extensible("BootResourcer") +public interface BootResourcer { + + int ORDER_CLASSLOADER = 0; + + int ORDER_TOMCAT = 10; + + /** + * Finds the resource with the given name and returns its input stream. + * + * @param resource The name of the resource to find. + * @return The input stream of the resource, or null if the resource could not be found. + */ + InputStream getResource(String resource) throws IOException; +} \ No newline at end of file diff --git a/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/loader/ClassloaderBootResourcer.java b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/loader/ClassloaderBootResourcer.java new file mode 100644 index 000000000..2d7a4e6d7 --- /dev/null +++ b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/loader/ClassloaderBootResourcer.java @@ -0,0 +1,43 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.core.bootstrap.resource.loader; + +import com.jd.live.agent.core.bootstrap.resource.BootResourcer; +import com.jd.live.agent.core.extension.annotation.Extension; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * A class that implements the ResourceFinder interface by searching for resources on the classpath. + */ +@Extension(value = "ClassloaderBootResourcer", order = BootResourcer.ORDER_CLASSLOADER) +public class ClassloaderBootResourcer implements BootResourcer { + + @Override + public InputStream getResource(String resource) throws IOException { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URL url = contextClassLoader.getResource(resource); + if (url == null) { + ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + if (systemClassLoader != contextClassLoader) { + url = systemClassLoader.getResource(resource); + } + } + return url == null ? null : url.openStream(); + } +} diff --git a/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/tomcat/TomcatBootResourcer.java b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/tomcat/TomcatBootResourcer.java new file mode 100644 index 000000000..ab5b23e18 --- /dev/null +++ b/joylive-core/joylive-core-framework/src/main/java/com/jd/live/agent/core/bootstrap/resource/tomcat/TomcatBootResourcer.java @@ -0,0 +1,102 @@ +/* + * Copyright © ${year} ${owner} (${email}) + * + * 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 + * + * http://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. + */ +package com.jd.live.agent.core.bootstrap.resource.tomcat; + +import com.jd.live.agent.core.bootstrap.resource.BootResourcer; +import com.jd.live.agent.core.extension.annotation.Extension; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A class that implements the ResourceFinder interface by searching for resources in a Tomcat web application. + */ +@Extension(value = "TomcatBootResourcer", order = BootResourcer.ORDER_TOMCAT) +public class TomcatBootResourcer implements BootResourcer { + + @Override + public InputStream getResource(String resource) throws IOException { + File workingDirectory = new File(System.getProperty("user.dir")); + if (workingDirectory.getName().equals("bin")) { + workingDirectory = workingDirectory.getParentFile(); + } + File webappDirectory = new File(workingDirectory, "webapps/"); + if (!webappDirectory.exists()) { + String home = System.getProperty("catalina.home"); + if (home != null) { + webappDirectory = new File(home, "webapps/"); + } + } + if (webappDirectory.exists()) { + String name = "WEB-INF/classes/" + resource; + File[] files = webappDirectory.listFiles(); + if (files != null) { + for (File file : files) { + InputStream inputStream = getInputStream(file, name, webappDirectory); + if (inputStream != null) { + return inputStream; + } + } + } + } + return null; + } + + /** + * A private helper method that tries to find the resource in a given file. + * + * @param file The file to search in. + * @param name The name of the resource to find. + * @param webappDirectory The web application directory. + * @return The input stream of the resource, or null if the resource could not be found. + * @throws IOException If an I/O error occurs while trying to read the resource. + */ + private InputStream getInputStream(File file, String name, File webappDirectory) throws IOException { + if (file.isFile() && file.getName().endsWith(".war")) { + return getInputStream(file, name); + } else if (file.isDirectory()) { + File resourceFile = new File(file, name); + return !resourceFile.exists() ? null : Files.newInputStream(resourceFile.toPath()); + } + return null; + } + + /** + * A private helper method that tries to find the resource in a given WAR file. + * + * @param file The WAR file to search in. + * @param name The name of the resource to find. + * @return The input stream of the resource, or null if the resource could not be found. + * @throws IOException If an I/O error occurs while trying to read the resource. + */ + private InputStream getInputStream(File file, String name) throws IOException { + try (JarFile jarFile = new JarFile(file)) { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.getName().equals(name)) { + return jarFile.getInputStream(entry); + } + } + } + return null; + } +} diff --git a/joylive-core/joylive-core-framework/src/main/resources/META-INF/services/com.jd.live.agent.core.bootstrap.resource.BootResourcer b/joylive-core/joylive-core-framework/src/main/resources/META-INF/services/com.jd.live.agent.core.bootstrap.resource.BootResourcer new file mode 100644 index 000000000..a2c1823cc --- /dev/null +++ b/joylive-core/joylive-core-framework/src/main/resources/META-INF/services/com.jd.live.agent.core.bootstrap.resource.BootResourcer @@ -0,0 +1,2 @@ +com.jd.live.agent.core.bootstrap.resource.loader.ClassloaderBootResourcer +com.jd.live.agent.core.bootstrap.resource.tomcat.TomcatBootResourcer \ No newline at end of file diff --git a/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/pom.xml b/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/pom.xml index bfcc6c7a6..dfc89176e 100644 --- a/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/pom.xml +++ b/joylive-demo/joylive-demo-springcloud2021/joylive-demo-springcloud2021-provider-war/pom.xml @@ -63,7 +63,7 @@ ${basedir}/target - joylive-demo-springcloud2021-provider + joylive-demo-springcloud2021-provider-war org.springframework.boot From 8840f4c131a2c5fd0ce2ab4701dfa81f9db1474f Mon Sep 17 00:00:00 2001 From: hexiaofeng Date: Thu, 16 Jan 2025 17:30:31 +0800 Subject: [PATCH 3/3] Add log --- .../springboot/v2/interceptor/NacosRegistrationInterceptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java index 4aeb0ab4b..501fce56d 100644 --- a/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java +++ b/joylive-plugin/joylive-application/joylive-application-springboot2/src/main/java/com/jd/live/agent/plugin/application/springboot/v2/interceptor/NacosRegistrationInterceptor.java @@ -55,6 +55,7 @@ public void onStarted(AppContext appContext) { registration.setPort(port); } registration.start(); + logger.info("Success starting nacos registration."); } catch (Throwable e) { logger.error("Failed to start nacos registration, caused by " + e.getMessage(), e); }