diff --git a/docs/README.md b/docs/README.md index de6855c..158562f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,4 +3,5 @@ * [Testing frameworks support](test_frameworks_support.md) * [Customization](customization.md) * [How it works](how_it_works.md) +* [Spring support](spring_support.md) * [Writing custom integrations](custom_integrations.md) diff --git a/docs/spring_support.md b/docs/spring_support.md new file mode 100644 index 0000000..4889e25 --- /dev/null +++ b/docs/spring_support.md @@ -0,0 +1,26 @@ +# Spring support + +BlockHound Spring module will automatically install the agent at a very early stage of Spring Boot application's startup. + +## Getting it +Download it from repo.spring.io or Maven Central repositories (stable releases only): + +```groovy +repositories { + maven { url 'https://repo.spring.io/milestone' } + // maven { url 'https://repo.spring.io/snapshot' } +} + +dependencies { + testCompile 'io.projectreactor.tools:blockhound-spring:$LATEST_RELEASE' + // testCompile 'io.projectreactor.tools:blockhound-spring:$LATEST_SNAPSHOT' +} +``` +Where: +`$LATEST_RELEASE` is: ![](https://img.shields.io/maven-metadata/v/https/repo.spring.io/milestone/io/projectreactor/tools/blockhound-spring/maven-metadata.xml.svg?label=) +`$LATEST_SNAPSHOT` is: ![](https://img.shields.io/maven-metadata/v/https/repo.spring.io/snapshot/io/projectreactor/tools/blockhound-spring/maven-metadata.xml.svg?label=) + +## Custom config +The listener will call `install()` without the arguments (see [customization](./customization.md) for details). + +If you want to apply your custom integration, register it as any other integration as documented [here](./custom_integrations.md). diff --git a/settings.gradle b/settings.gradle index 4ccb5b3..b35447e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,5 +3,6 @@ rootProject.name = 'reactor-block-hound' include 'agent' include 'native-agent' +include 'spring' include 'example' include 'junit-platform' diff --git a/spring/build.gradle b/spring/build.gradle new file mode 100644 index 0000000..3b899be --- /dev/null +++ b/spring/build.gradle @@ -0,0 +1,42 @@ +plugins { + id "java" + id "maven-publish" +} + +sourceCompatibility = targetCompatibility = 8 + +repositories { + jcenter() +} + +dependencies { + compile project(path: ":agent", configuration: 'shadow') + + compileOnly 'org.springframework.boot:spring-boot:2.1.3.RELEASE' + + testCompile 'junit:junit:4.12' + testCompile 'org.assertj:assertj-core:3.11.1' + testCompile 'org.springframework.boot:spring-boot:2.1.3.RELEASE' + testCompile 'io.projectreactor:reactor-core:3.2.2.RELEASE' +} + +test { + forkEvery = 1 + maxParallelForks = 1 +} + +task sourcesJar(type: Jar) { + classifier 'sources' + from sourceSets.main.allJava +} + +publishing { + publications { + mavenJava(MavenPublication) { publication -> + from components.java + artifact sourcesJar + + artifactId 'blockhound-spring' + } + } +} diff --git a/spring/src/main/java/reactor/blockhound/spring/BlockHoundRunListener.java b/spring/src/main/java/reactor/blockhound/spring/BlockHoundRunListener.java new file mode 100644 index 0000000..9b8d913 --- /dev/null +++ b/spring/src/main/java/reactor/blockhound/spring/BlockHoundRunListener.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019-Present Pivotal Software Inc, All Rights Reserved. + * + * 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. + */ +package reactor.blockhound.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringApplicationRunListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; +import reactor.BlockHound; +import reactor.blockhound.integration.BlockHoundIntegration; + +/** + * This {@link SpringApplicationRunListener} installs the agent when an app is starting + * by calling {@link BlockHound#install(BlockHoundIntegration...)}. + */ +public class BlockHoundRunListener implements SpringApplicationRunListener, Ordered { + + public BlockHoundRunListener(SpringApplication application, String[] args) { + + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + @Override + public void starting() { + BlockHound.install(); + } + + @Override + public void environmentPrepared(ConfigurableEnvironment environment) { + + } + + @Override + public void contextPrepared(ConfigurableApplicationContext context) { + + } + + @Override + public void contextLoaded(ConfigurableApplicationContext context) { + + } + + @Override + public void started(ConfigurableApplicationContext context) { + + } + + @Override + public void running(ConfigurableApplicationContext context) { + + } + + @Override + public void failed(ConfigurableApplicationContext context, Throwable exception) { + + } +} diff --git a/spring/src/main/resources/META-INF/spring.factories b/spring/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..e245841 --- /dev/null +++ b/spring/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Run Listeners +org.springframework.boot.SpringApplicationRunListener=\ +reactor.blockhound.spring.BlockHoundRunListener \ No newline at end of file diff --git a/spring/src/test/java/reactor/blockhound/spring/BlockHoundRunListenerTest.java b/spring/src/test/java/reactor/blockhound/spring/BlockHoundRunListenerTest.java new file mode 100644 index 0000000..1edcbc7 --- /dev/null +++ b/spring/src/test/java/reactor/blockhound/spring/BlockHoundRunListenerTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019-Present Pivotal Software Inc, All Rights Reserved. + * + * 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. + */ +package reactor.blockhound.spring; + +import org.assertj.core.api.AbstractThrowableAssert; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.context.annotation.Configuration; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import java.io.Closeable; + +public class BlockHoundRunListenerTest { + + @Test + public void shouldInstallOnRun() throws Exception { + assertThatBlockingCall().doesNotThrowAnyException(); + + try (Closeable __ = new SpringApplication(DummyAppConfiguration.class).run()) { + assertThatBlockingCall().hasMessageContaining("Blocking call!"); + } + } + + private AbstractThrowableAssert assertThatBlockingCall() { + return Assertions.assertThatCode(() -> { + Mono + .fromCallable(() -> { + Thread.sleep(1); + return ""; + }) + .subscribeOn(Schedulers.parallel()) + .block(); + }); + } + + @Configuration + static class DummyAppConfiguration { + } + +}