Skip to content

Commit cfc15b7

Browse files
fix: Unify Bookmarkmanager creation and usage for good.
1 parent f759c5a commit cfc15b7

File tree

13 files changed

+500
-36
lines changed

13 files changed

+500
-36
lines changed

src/main/asciidoc/faq/index.adoc

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -734,33 +734,28 @@ WARNING: Use this bookmark manager at your own risk, it will effectively disable
734734
In a cluster this can be a sensible approach only and if only you can tolerate stale reads and are not in danger of
735735
overwriting old data.
736736

737-
You need to provide the following configuration in your system and make sure that SDN uses the transaction manager:
737+
The following configuration creates a "noop" variant of the bookmark manager that will be picked up from relevant classes.
738738

739739
[source,java,indent=0,tabsize=4]
740740
.BookmarksDisabledConfig.java
741741
----
742742
import org.neo4j.driver.Driver;
743743
import org.springframework.context.annotation.Bean;
744744
import org.springframework.context.annotation.Configuration;
745-
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
746745
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
747-
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
748-
import org.springframework.transaction.PlatformTransactionManager;
749746
750747
@Configuration
751748
public class BookmarksDisabledConfig {
752749
753750
@Bean
754-
public PlatformTransactionManager transactionManager(
755-
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
751+
public Neo4jBookmarkManager neo4jBookmarkManager() {
756752
757-
Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop(); // <.>
758-
return new Neo4jTransactionManager(
759-
driver, databaseNameProvider, bookmarkManager);
753+
return Neo4jBookmarkManager.noop();
760754
}
761755
}
762756
----
763-
<.> Get an instance of the Noop bookmark manager
757+
758+
You can configure the pairs of `Neo4jTransactionManager/Neo4jClient` and `ReactiveNeo4jTransactionManager/ReactiveNeo4jClient` individually, but we recommend in doing so only when you already configuring them for specific database selection needs.
764759

765760
[[faq.annotations.specific]]
766761
== Do I need to use Neo4j specific annotations?

src/main/java/org/springframework/data/neo4j/core/DefaultNeo4jClient.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@
3535
import org.neo4j.driver.Value;
3636
import org.neo4j.driver.summary.ResultSummary;
3737
import org.neo4j.driver.types.TypeSystem;
38+
import org.springframework.beans.BeansException;
39+
import org.springframework.context.ApplicationContext;
40+
import org.springframework.context.ApplicationContextAware;
3841
import org.springframework.core.convert.ConversionService;
3942
import org.springframework.core.convert.converter.ConverterRegistry;
4043
import org.springframework.core.convert.support.DefaultConversionService;
4144
import org.springframework.dao.DataAccessException;
4245
import org.springframework.dao.support.PersistenceExceptionTranslator;
4346
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
47+
import org.springframework.data.neo4j.core.support.BookmarkManagerReference;
4448
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
4549
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
4650
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
@@ -56,7 +60,7 @@
5660
* @author Michael J. Simons
5761
* @since 6.0
5862
*/
59-
final class DefaultNeo4jClient implements Neo4jClient {
63+
final class DefaultNeo4jClient implements Neo4jClient, ApplicationContextAware {
6064

6165
private final Driver driver;
6266
private @Nullable final DatabaseSelectionProvider databaseSelectionProvider;
@@ -65,14 +69,14 @@ final class DefaultNeo4jClient implements Neo4jClient {
6569
private final Neo4jPersistenceExceptionTranslator persistenceExceptionTranslator = new Neo4jPersistenceExceptionTranslator();
6670

6771
// Local bookmark manager when using outside managed transactions
68-
private final Neo4jBookmarkManager bookmarkManager;
72+
private final BookmarkManagerReference bookmarkManager;
6973

7074
DefaultNeo4jClient(Builder builder) {
7175

7276
this.driver = builder.driver;
7377
this.databaseSelectionProvider = builder.databaseSelectionProvider;
7478
this.userSelectionProvider = builder.userSelectionProvider;
75-
this.bookmarkManager = builder.bookmarkManager != null ? builder.bookmarkManager : Neo4jBookmarkManager.create();
79+
this.bookmarkManager = new BookmarkManagerReference(Neo4jBookmarkManager::create, builder.bookmarkManager);
7680

7781
this.conversionService = new DefaultConversionService();
7882
Optional.ofNullable(builder.neo4jConversions).orElseGet(Neo4jConversions::new).registerConvertersIn((ConverterRegistry) conversionService);
@@ -82,13 +86,19 @@ final class DefaultNeo4jClient implements Neo4jClient {
8286
public QueryRunner getQueryRunner(DatabaseSelection databaseSelection, UserSelection impersonatedUser) {
8387

8488
QueryRunner queryRunner = Neo4jTransactionManager.retrieveTransaction(driver, databaseSelection, impersonatedUser);
85-
Collection<Bookmark> lastBookmarks = bookmarkManager.getBookmarks();
89+
Collection<Bookmark> lastBookmarks = bookmarkManager.resolve().getBookmarks();
8690

8791
if (queryRunner == null) {
8892
queryRunner = driver.session(Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, databaseSelection, impersonatedUser));
8993
}
9094

91-
return new DelegatingQueryRunner(queryRunner, lastBookmarks, bookmarkManager::updateBookmarks);
95+
return new DelegatingQueryRunner(queryRunner, lastBookmarks, bookmarkManager.resolve()::updateBookmarks);
96+
}
97+
98+
@Override
99+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
100+
101+
this.bookmarkManager.setApplicationContext(applicationContext);
92102
}
93103

94104
private static class DelegatingQueryRunner implements QueryRunner {

src/main/java/org/springframework/data/neo4j/core/DefaultReactiveNeo4jClient.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@
2626
import org.neo4j.driver.summary.ResultSummary;
2727
import org.neo4j.driver.types.TypeSystem;
2828
import org.reactivestreams.Publisher;
29+
import org.springframework.beans.BeansException;
30+
import org.springframework.context.ApplicationContext;
31+
import org.springframework.context.ApplicationContextAware;
2932
import org.springframework.core.convert.ConversionService;
3033
import org.springframework.core.convert.converter.ConverterRegistry;
3134
import org.springframework.core.convert.support.DefaultConversionService;
3235
import org.springframework.dao.DataAccessException;
3336
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
37+
import org.springframework.data.neo4j.core.support.BookmarkManagerReference;
3438
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
3539
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
3640
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
@@ -59,7 +63,7 @@
5963
* @soundtrack Die Toten Hosen - Im Auftrag des Herrn
6064
* @since 6.0
6165
*/
62-
final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient {
66+
final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient, ApplicationContextAware {
6367

6468
private final Driver driver;
6569
private @Nullable final ReactiveDatabaseSelectionProvider databaseSelectionProvider;
@@ -68,7 +72,7 @@ final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient {
6872
private final Neo4jPersistenceExceptionTranslator persistenceExceptionTranslator = new Neo4jPersistenceExceptionTranslator();
6973

7074
// Local bookmark manager when using outside managed transactions
71-
private final Neo4jBookmarkManager bookmarkManager;
75+
private final BookmarkManagerReference bookmarkManager;
7276

7377
DefaultReactiveNeo4jClient(Builder builder) {
7478

@@ -78,7 +82,7 @@ final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient {
7882

7983
this.conversionService = new DefaultConversionService();
8084
Optional.ofNullable(builder.neo4jConversions).orElseGet(Neo4jConversions::new).registerConvertersIn((ConverterRegistry) conversionService);
81-
this.bookmarkManager = builder.bookmarkManager != null ? builder.bookmarkManager : Neo4jBookmarkManager.createReactive();
85+
this.bookmarkManager = new BookmarkManagerReference(Neo4jBookmarkManager::createReactive, builder.bookmarkManager);
8286
}
8387

8488
@Override
@@ -88,12 +92,18 @@ public Mono<ReactiveQueryRunner> getQueryRunner(Mono<DatabaseSelection> database
8892
.flatMap(targetDatabaseAndUser ->
8993
ReactiveNeo4jTransactionManager.retrieveReactiveTransaction(driver, targetDatabaseAndUser.getT1(), targetDatabaseAndUser.getT2())
9094
.map(ReactiveQueryRunner.class::cast)
91-
.zipWith(Mono.just(bookmarkManager.getBookmarks()))
95+
.zipWith(Mono.just(bookmarkManager.resolve().getBookmarks()))
9296
.switchIfEmpty(Mono.fromSupplier(() -> {
93-
Collection<Bookmark> lastBookmarks = bookmarkManager.getBookmarks();
97+
Collection<Bookmark> lastBookmarks = bookmarkManager.resolve().getBookmarks();
9498
return Tuples.of(driver.session(ReactiveSession.class, Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, targetDatabaseAndUser.getT1(), targetDatabaseAndUser.getT2())), lastBookmarks);
9599
})))
96-
.map(t -> new DelegatingQueryRunner(t.getT1(), t.getT2(), bookmarkManager::updateBookmarks));
100+
.map(t -> new DelegatingQueryRunner(t.getT1(), t.getT2(), bookmarkManager.resolve()::updateBookmarks));
101+
}
102+
103+
@Override
104+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
105+
106+
bookmarkManager.setApplicationContext(applicationContext);
97107
}
98108

99109
private static class DelegatingQueryRunner implements ReactiveQueryRunner {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2011-2023 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.neo4j.core.support;
17+
18+
import java.util.function.Supplier;
19+
20+
import org.springframework.beans.BeansException;
21+
import org.springframework.beans.factory.BeanCreationException;
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.ApplicationContextAware;
25+
import org.springframework.context.ApplicationEventPublisher;
26+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
27+
import org.springframework.lang.Nullable;
28+
29+
/**
30+
* Don't use outside SDN code. You have been warned.
31+
*
32+
* @author Michael J. Simons
33+
*/
34+
public final class BookmarkManagerReference implements ApplicationContextAware {
35+
36+
private final Supplier<Neo4jBookmarkManager> defaultBookmarkManagerSupplier;
37+
38+
private ObjectProvider<Neo4jBookmarkManager> neo4jBookmarkManagers = new ObjectProvider<Neo4jBookmarkManager>() {
39+
@Override
40+
public Neo4jBookmarkManager getObject(Object... args) throws BeansException {
41+
throw new BeanCreationException("This provider can't create new beans");
42+
}
43+
44+
@Override
45+
public Neo4jBookmarkManager getIfAvailable() throws BeansException {
46+
return null;
47+
}
48+
49+
@Override
50+
public Neo4jBookmarkManager getIfUnique() throws BeansException {
51+
return null;
52+
}
53+
54+
@Override
55+
public Neo4jBookmarkManager getObject() throws BeansException {
56+
throw new BeanCreationException("This provider can't create new beans");
57+
}
58+
};
59+
60+
@Nullable
61+
private volatile Neo4jBookmarkManager bookmarkManager;
62+
63+
private ApplicationEventPublisher applicationEventPublisher;
64+
65+
public BookmarkManagerReference(Supplier<Neo4jBookmarkManager> defaultBookmarkManagerSupplier, @Nullable Neo4jBookmarkManager bookmarkManager) {
66+
this.defaultBookmarkManagerSupplier = defaultBookmarkManagerSupplier;
67+
this.bookmarkManager = bookmarkManager;
68+
}
69+
70+
@Override
71+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
72+
73+
this.neo4jBookmarkManagers = applicationContext.getBeanProvider(Neo4jBookmarkManager.class);
74+
this.applicationEventPublisher = applicationContext;
75+
if (this.bookmarkManager != null) {
76+
this.bookmarkManager.setApplicationEventPublisher(this.applicationEventPublisher);
77+
}
78+
}
79+
80+
public Neo4jBookmarkManager resolve() {
81+
Neo4jBookmarkManager result = this.bookmarkManager;
82+
if (result == null) {
83+
synchronized (this) {
84+
result = this.bookmarkManager;
85+
if (result == null) {
86+
this.bookmarkManager = neo4jBookmarkManagers.getIfAvailable(this.defaultBookmarkManagerSupplier);
87+
this.bookmarkManager.setApplicationEventPublisher(this.applicationEventPublisher);
88+
result = this.bookmarkManager;
89+
}
90+
}
91+
}
92+
return result;
93+
}
94+
}

src/main/java/org/springframework/data/neo4j/core/transaction/Neo4jTransactionManager.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
3131
import org.springframework.data.neo4j.core.UserSelection;
3232
import org.springframework.data.neo4j.core.UserSelectionProvider;
33+
import org.springframework.data.neo4j.core.support.BookmarkManagerReference;
3334
import org.springframework.lang.Nullable;
3435
import org.springframework.transaction.TransactionDefinition;
3536
import org.springframework.transaction.TransactionException;
@@ -136,7 +137,7 @@ public Neo4jTransactionManager build() {
136137
*/
137138
private final UserSelectionProvider userSelectionProvider;
138139

139-
private final Neo4jBookmarkManager bookmarkManager;
140+
private final BookmarkManagerReference bookmarkManager;
140141

141142
/**
142143
* This will create a transaction manager for the default database.
@@ -181,14 +182,13 @@ private Neo4jTransactionManager(Builder builder) {
181182
this.userSelectionProvider = builder.userSelectionProvider == null ?
182183
UserSelectionProvider.getDefaultSelectionProvider() :
183184
builder.userSelectionProvider;
184-
this.bookmarkManager =
185-
builder.bookmarkManager == null ? Neo4jBookmarkManager.create() : builder.bookmarkManager;
185+
this.bookmarkManager = new BookmarkManagerReference(Neo4jBookmarkManager::create, builder.bookmarkManager);
186186
}
187187

188188
@Override
189189
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
190190

191-
this.bookmarkManager.setApplicationEventPublisher(applicationContext);
191+
this.bookmarkManager.setApplicationContext(applicationContext);
192192
}
193193

194194
/**
@@ -298,7 +298,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) thr
298298
try {
299299
// Prepare configuration data
300300
Neo4jTransactionContext context = new Neo4jTransactionContext(
301-
databaseSelectionProvider.getDatabaseSelection(), userSelectionProvider.getUserSelection(), bookmarkManager.getBookmarks());
301+
databaseSelectionProvider.getDatabaseSelection(), userSelectionProvider.getUserSelection(), bookmarkManager.resolve().getBookmarks());
302302

303303
// Configure and open session together with a native transaction
304304
Session session = this.driver.session(
@@ -341,7 +341,7 @@ protected void doCommit(DefaultTransactionStatus status) throws TransactionExcep
341341
Neo4jTransactionObject transactionObject = extractNeo4jTransaction(status);
342342
Neo4jTransactionHolder transactionHolder = transactionObject.getRequiredResourceHolder();
343343
Collection<Bookmark> newBookmarks = transactionHolder.commit();
344-
this.bookmarkManager.updateBookmarks(transactionHolder.getBookmarks(), newBookmarks);
344+
this.bookmarkManager.resolve().updateBookmarks(transactionHolder.getBookmarks(), newBookmarks);
345345
}
346346

347347
@Override

src/main/java/org/springframework/data/neo4j/core/transaction/ReactiveNeo4jTransactionManager.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider;
3131
import org.springframework.data.neo4j.core.ReactiveUserSelectionProvider;
3232
import org.springframework.data.neo4j.core.UserSelection;
33+
import org.springframework.data.neo4j.core.support.BookmarkManagerReference;
3334
import org.springframework.lang.Nullable;
3435
import org.springframework.transaction.NoTransactionException;
3536
import org.springframework.transaction.TransactionDefinition;
@@ -133,7 +134,7 @@ public ReactiveNeo4jTransactionManager build() {
133134
*/
134135
private final ReactiveUserSelectionProvider userSelectionProvider;
135136

136-
private final Neo4jBookmarkManager bookmarkManager;
137+
private final BookmarkManagerReference bookmarkManager;
137138

138139
/**
139140
* This will create a transaction manager for the default database.
@@ -178,14 +179,13 @@ private ReactiveNeo4jTransactionManager(Builder builder) {
178179
this.userSelectionProvider = builder.userSelectionProvider == null ?
179180
ReactiveUserSelectionProvider.getDefaultSelectionProvider() :
180181
builder.userSelectionProvider;
181-
this.bookmarkManager =
182-
builder.bookmarkManager == null ? Neo4jBookmarkManager.createReactive() : builder.bookmarkManager;
182+
this.bookmarkManager = new BookmarkManagerReference(Neo4jBookmarkManager::createReactive, builder.bookmarkManager);
183183
}
184184

185185
@Override
186186
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
187187

188-
this.bookmarkManager.setApplicationEventPublisher(applicationContext);
188+
this.bookmarkManager.setApplicationContext(applicationContext);
189189
}
190190

191191
/**
@@ -292,7 +292,7 @@ protected Mono<Void> doBegin(TransactionSynchronizationManager transactionSynchr
292292
userSelectionProvider
293293
.getUserSelection()
294294
.switchIfEmpty(Mono.just(UserSelection.connectedUser())),
295-
(databaseSelection, userSelection) -> new Neo4jTransactionContext(databaseSelection, userSelection, bookmarkManager.getBookmarks()))
295+
(databaseSelection, userSelection) -> new Neo4jTransactionContext(databaseSelection, userSelection, bookmarkManager.resolve().getBookmarks()))
296296
.map(context -> Tuples.of(context, this.driver.session(ReactiveSession.class, Neo4jTransactionUtils.sessionConfig(readOnly, context.getBookmarks(), context.getDatabaseSelection(), context.getUserSelection()))))
297297
.flatMap(contextAndSession -> Mono.fromDirect(contextAndSession.getT2().beginTransaction(transactionConfig)).single()
298298
.map(nativeTransaction -> new ReactiveNeo4jTransactionHolder(contextAndSession.getT1(),
@@ -325,7 +325,7 @@ protected Mono<Void> doCommit(TransactionSynchronizationManager transactionSynch
325325
ReactiveNeo4jTransactionHolder holder = extractNeo4jTransaction(genericReactiveTransaction)
326326
.getRequiredResourceHolder();
327327
return holder.commit()
328-
.doOnNext(bookmark -> bookmarkManager.updateBookmarks(holder.getBookmarks(), bookmark))
328+
.doOnNext(bookmark -> bookmarkManager.resolve().updateBookmarks(holder.getBookmarks(), bookmark))
329329
.then();
330330
}
331331

src/test/java/org/springframework/data/neo4j/core/transaction/Neo4jTransactionManagerTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.springframework.data.neo4j.core.DatabaseSelection;
5353
import org.springframework.data.neo4j.core.UserSelection;
5454
import org.springframework.data.neo4j.core.Neo4jClient;
55+
import org.springframework.data.neo4j.core.support.BookmarkManagerReference;
5556
import org.springframework.transaction.TransactionDefinition;
5657
import org.springframework.transaction.TransactionStatus;
5758
import org.springframework.transaction.jta.JtaTransactionManager;
@@ -152,7 +153,7 @@ private void injectBookmarkManager(Neo4jTransactionManager txManager, Neo4jBookm
152153
throws NoSuchFieldException, IllegalAccessException {
153154
Field bookmarkManager = Neo4jTransactionManager.class.getDeclaredField("bookmarkManager");
154155
bookmarkManager.setAccessible(true);
155-
bookmarkManager.set(txManager, value);
156+
bookmarkManager.set(txManager, new BookmarkManagerReference(Neo4jBookmarkManager::create, value));
156157
}
157158

158159
@Nested

0 commit comments

Comments
 (0)