Skip to content

Commit fe4ab0c

Browse files
committed
Polishing.
Introduce factory beans for easier wrapping of CqlSession. See #1321 Original pull request: #1322
1 parent 1d3e754 commit fe4ab0c

File tree

5 files changed

+203
-35
lines changed

5 files changed

+203
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.cassandra.observability;
17+
18+
import org.springframework.beans.factory.config.AbstractFactoryBean;
19+
import org.springframework.lang.Nullable;
20+
import org.springframework.util.Assert;
21+
import org.springframework.util.ObjectUtils;
22+
23+
import com.datastax.oss.driver.api.core.CqlSession;
24+
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
25+
26+
import io.micrometer.observation.ObservationRegistry;
27+
28+
/**
29+
* Factory bean to construct a {@link CqlSession} integrated with given {@link ObservationRegistry}. This factory bean
30+
* registers also {@link ObservationRequestTracker#INSTANCE ObservationRequestTracker.INSTANCE} with the builder to
31+
* ensure full integration with the required infrastructure.
32+
*
33+
* @author Mark Paluch
34+
* @since 4.0
35+
* @see ObservationRequestTracker
36+
* @see ObservableCqlSessionFactory
37+
*/
38+
public class ObservableCqlSessionFactoryBean extends AbstractFactoryBean<CqlSession> {
39+
40+
private final CqlSessionBuilder cqlSessionBuilder;
41+
42+
private final ObservationRegistry observationRegistry;
43+
44+
private @Nullable String remoteServiceName;
45+
46+
/**
47+
* Construct a new {@link ObservableCqlSessionFactoryBean}.
48+
*
49+
* @param cqlSessionBuilder must not be {@literal null}.
50+
* @param observationRegistry must not be {@literal null}.
51+
*/
52+
public ObservableCqlSessionFactoryBean(CqlSessionBuilder cqlSessionBuilder, ObservationRegistry observationRegistry) {
53+
54+
Assert.notNull(cqlSessionBuilder, "CqlSessionBuilder must not be null");
55+
Assert.notNull(observationRegistry, "ObservationRegistry must not be null");
56+
57+
this.cqlSessionBuilder = cqlSessionBuilder;
58+
this.observationRegistry = observationRegistry;
59+
}
60+
61+
@Override
62+
protected CqlSession createInstance() {
63+
64+
cqlSessionBuilder.addRequestTracker(ObservationRequestTracker.INSTANCE);
65+
66+
if (ObjectUtils.isEmpty(getRemoteServiceName())) {
67+
return ObservableCqlSessionFactory.wrap(cqlSessionBuilder.build(), observationRegistry);
68+
}
69+
70+
return ObservableCqlSessionFactory.wrap(cqlSessionBuilder.build(), getRemoteServiceName(), observationRegistry);
71+
}
72+
73+
@Override
74+
protected void destroyInstance(@Nullable CqlSession instance) {
75+
if (instance != null) {
76+
instance.close();
77+
}
78+
}
79+
80+
@Override
81+
public Class<?> getObjectType() {
82+
return CqlSession.class;
83+
}
84+
85+
@Nullable
86+
public String getRemoteServiceName() {
87+
return remoteServiceName;
88+
}
89+
90+
/**
91+
* Set the remote service name.
92+
*
93+
* @param remoteServiceName
94+
*/
95+
public void setRemoteServiceName(@Nullable String remoteServiceName) {
96+
this.remoteServiceName = remoteServiceName;
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.cassandra.observability;
17+
18+
import org.springframework.beans.factory.config.AbstractFactoryBean;
19+
import org.springframework.data.cassandra.ReactiveSession;
20+
import org.springframework.data.cassandra.core.cql.session.DefaultBridgedReactiveSession;
21+
import org.springframework.lang.Nullable;
22+
import org.springframework.util.Assert;
23+
import org.springframework.util.ObjectUtils;
24+
25+
import com.datastax.oss.driver.api.core.CqlSession;
26+
27+
import io.micrometer.observation.ObservationRegistry;
28+
29+
/**
30+
* Factory bean to construct a {@link ReactiveSession} integrated with given {@link ObservationRegistry}. The required
31+
* {@link CqlSession} must be associated with {@link ObservationRequestTracker#INSTANCE
32+
* ObservationRequestTracker.INSTANCE} to ensure proper integration with all observability components. You can use
33+
* {@link ObservableCqlSessionFactoryBean} to obtain a properly configured {@link CqlSession}.
34+
*
35+
* @author Mark Paluch
36+
* @since 4.0
37+
* @see ObservationRequestTracker
38+
* @see ObservableReactiveSessionFactory
39+
*/
40+
public class ObservableReactiveSessionFactoryBean extends AbstractFactoryBean<ReactiveSession> {
41+
42+
private final CqlSession cqlSession;
43+
44+
private final ObservationRegistry observationRegistry;
45+
46+
private @Nullable String remoteServiceName;
47+
48+
/**
49+
* Construct a new {@link ObservableReactiveSessionFactoryBean}.
50+
*
51+
* @param cqlSession must not be {@literal null}.
52+
* @param observationRegistry must not be {@literal null}.
53+
*/
54+
public ObservableReactiveSessionFactoryBean(CqlSession cqlSession, ObservationRegistry observationRegistry) {
55+
56+
Assert.notNull(cqlSession, "CqlSession must not be null");
57+
Assert.notNull(observationRegistry, "ObservationRegistry must not be null");
58+
59+
this.cqlSession = cqlSession;
60+
this.observationRegistry = observationRegistry;
61+
}
62+
63+
@Override
64+
protected ReactiveSession createInstance() {
65+
66+
if (ObjectUtils.isEmpty(getRemoteServiceName())) {
67+
return ObservableReactiveSessionFactory.wrap(new DefaultBridgedReactiveSession(cqlSession), observationRegistry);
68+
}
69+
70+
return ObservableReactiveSessionFactory.wrap(new DefaultBridgedReactiveSession(cqlSession), getRemoteServiceName(),
71+
observationRegistry);
72+
}
73+
74+
@Override
75+
public Class<?> getObjectType() {
76+
return ReactiveSession.class;
77+
}
78+
79+
@Nullable
80+
public String getRemoteServiceName() {
81+
return remoteServiceName;
82+
}
83+
84+
/**
85+
* Set the remote service name.
86+
*
87+
* @param remoteServiceName
88+
*/
89+
public void setRemoteServiceName(@Nullable String remoteServiceName) {
90+
this.remoteServiceName = remoteServiceName;
91+
}
92+
}

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/observability/ReactiveIntegrationTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
@ContextConfiguration(classes = TestConfig.class)
4646
public class ReactiveIntegrationTests extends SampleTestRunner {
4747

48-
@Autowired ReactiveSession session;
48+
@Autowired ReactiveSession observableSession;
4949

5050
ReactiveIntegrationTests() {
5151
super(SampleRunnerConfig.builder().build());
@@ -68,7 +68,6 @@ public SampleTestRunnerConsumer yourCode() {
6868

6969
Observation intermediate = Observation.start("intermediate", createObservationRegistry());
7070

71-
ReactiveSession observableSession = ObservableReactiveSessionFactory.wrap(session, createObservationRegistry());
7271

7372
Mono<ReactiveResultSet> drop = observableSession.execute("DROP KEYSPACE IF EXISTS ObservationTest");
7473
Mono<ReactiveResultSet> create = observableSession.execute("CREATE KEYSPACE ObservationTest " + "WITH "

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/observability/TestConfig.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
import org.springframework.context.annotation.Bean;
1919
import org.springframework.core.io.ClassPathResource;
2020
import org.springframework.core.io.Resource;
21-
import org.springframework.data.cassandra.ReactiveSession;
2221
import org.springframework.data.cassandra.config.SessionBuilderConfigurer;
23-
import org.springframework.data.cassandra.core.cql.session.DefaultBridgedReactiveSession;
2422
import org.springframework.data.cassandra.support.AbstractTestJavaConfig;
2523
import org.springframework.lang.Nullable;
2624

@@ -71,7 +69,7 @@ protected Resource getDriverConfigurationResource() {
7169
}
7270

7371
@Bean
74-
ReactiveSession reactiveSession(CqlSession session) {
75-
return new DefaultBridgedReactiveSession(session);
72+
ObservableReactiveSessionFactoryBean reactiveSession(CqlSession session, ObservationRegistry observationRegistry) {
73+
return new ObservableReactiveSessionFactoryBean(session, observationRegistry);
7674
}
7775
}

src/main/asciidoc/reference/observability.adoc

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,22 @@ To enable the instrumentation, apply the following configuration to your applica
1414
class ObservabilityConfiguration {
1515
1616
@Bean
17-
public ObservationBeanPostProcessor observationBeanPostProcessor(ObservationRegistry observationRegistry) {
18-
return new ObservationBeanPostProcessor(observationRegistry); <1>
17+
public ObservableCqlSessionFactoryBean observableCqlSession(CqlSessionBuilder builder,
18+
ObservationRegistry registry) {
19+
return new ObservableCqlSessionFactoryBean(builder, registry); <1>
1920
}
2021
2122
@Bean
22-
public SessionBuilderConfigurer getSessionBuilderConfigurer() {
23-
return sessionBuilder -> sessionBuilder.addRequestTracker(ObservationRequestTracker.INSTANCE); <2>
24-
}
25-
26-
class ObservationBeanPostProcessor implements BeanPostProcessor {
27-
28-
public final ObservationRegistry observationRegistry;
29-
30-
public ObservationBeanPostProcessor(ObservationRegistry observationRegistry) {
31-
this.observationRegistry = observationRegistry;
32-
}
33-
34-
@Override
35-
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
36-
37-
if (bean instanceof CqlSession) {
38-
return ObservableCqlSessionFactory.wrap((CqlSession) bean, observationRegistry);
39-
}
40-
41-
if (bean instanceof ReactiveSession) {
42-
return ObservableReactiveSessionFactory.wrap((ReactiveSession) bean, observationRegistry);
43-
}
44-
45-
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
46-
}
23+
public ObservableReactiveSessionFactoryBean observableReactiveSession(CqlSession session,
24+
ObservationRegistry registry) {
25+
return new ObservableReactiveSessionFactoryBean(session, registry); <2>
4726
}
4827
}
4928
----
50-
<1> Wraps all CQL session objects (imperative/reactive Session API) to observe Cassandra statement execution.
51-
<2> Integrate with the Cassandra driver to obtain success/error callbacks.
29+
30+
<1> Wraps the CQL session object to observe Cassandra statement execution.
31+
Also, registers `ObservationRequestTracker.INSTANCE` with the `CqlSessionBuilder`.
32+
<1> Wraps a CQL session object to observe reactive Cassandra statement execution.
5233
====
5334

5435
include::../../../../target/_conventions.adoc[]

0 commit comments

Comments
 (0)