Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
26 changes: 26 additions & 0 deletions docs/spring_support.md
Original file line number Diff line number Diff line change
@@ -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).
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ rootProject.name = 'reactor-block-hound'

include 'agent'
include 'native-agent'
include 'spring'
include 'example'
include 'junit-platform'
42 changes: 42 additions & 0 deletions spring/build.gradle
Original file line number Diff line number Diff line change
@@ -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'
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philwebb does it have to be public, or I can make it internal?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philwebb, has the Boot team considered making the methods in SpringApplicationRunListener interface default methods now that Java 8 is the baseline?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, will add an issue. That API is somewhat unusual as it mainly exists to break a package tangle.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, will add an issue.

Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philwebb anything to say about the visibility of this class? 😊


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) {

}
}
3 changes: 3 additions & 0 deletions spring/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
reactor.blockhound.spring.BlockHoundRunListener
Original file line number Diff line number Diff line change
@@ -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<?, ? extends Throwable> assertThatBlockingCall() {
return Assertions.assertThatCode(() -> {
Mono
.fromCallable(() -> {
Thread.sleep(1);
return "";
})
.subscribeOn(Schedulers.parallel())
.block();
});
}

@Configuration
static class DummyAppConfiguration {
}

}