Skip to content

Commit c6508b3

Browse files
committed
Polishing.
Rename CodecFinder to CodecLookup. Rename default implementations to CachedCodecLookup and DefaultCodecLookup. Extract CodecMetadata interface and turn getFormats() into a default method. Refactor how CodecLookup obtains its actual codecs to prevent methods allowing to alter the internal state of the cache component through updateCodecs(…). The delegate is typically a CodecRegistry for iteration over the actual codecs. Reinstate socket tests on MacOS as sockets are supported on BSD via kqueue. Remove overly complex spy arrangements from tests. Refine tests. [resolves #444][#409] Signed-off-by: Mark Paluch <[email protected]>
1 parent 751b091 commit c6508b3

18 files changed

+424
-388
lines changed

src/main/java/io/r2dbc/postgresql/codec/AbstractCodec.java

+1-10
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,17 @@
2626
import reactor.core.publisher.Mono;
2727
import reactor.util.annotation.Nullable;
2828

29-
import java.util.EnumSet;
3029
import java.util.function.Supplier;
3130

3231
import static io.r2dbc.postgresql.client.Parameter.NULL_VALUE;
33-
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
34-
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
3532

3633
/**
3734
* Abstract codec class that provides a basis for all concrete
3835
* implementations of a {@link Codec} for well-known {@link PostgresqlObjectId}.
3936
*
4037
* @param <T> the type that is handled by this {@link Codec}
4138
*/
42-
abstract class AbstractCodec<T> implements Codec<T> {
39+
abstract class AbstractCodec<T> implements Codec<T>, CodecMetadata {
4340

4441
private final Class<T> type;
4542

@@ -98,12 +95,6 @@ public Class<?> type() {
9895
return this.type;
9996
}
10097

101-
@Override
102-
public Iterable<Format> getFormats() {
103-
// Unless overridden all codecs supports both text and binary format
104-
return EnumSet.of(FORMAT_TEXT, FORMAT_BINARY);
105-
}
106-
10798
/**
10899
* Create a {@link Parameter}.
109100
*

src/main/java/io/r2dbc/postgresql/codec/CodecFinderCacheImpl.java renamed to src/main/java/io/r2dbc/postgresql/codec/CachedCodecLookup.java

+75-74
Original file line numberDiff line numberDiff line change
@@ -22,144 +22,145 @@
2222
import reactor.util.Logger;
2323
import reactor.util.Loggers;
2424

25-
import java.util.Collections;
26-
import java.util.List;
25+
import java.util.Iterator;
2726
import java.util.Map;
28-
import java.util.Optional;
2927
import java.util.concurrent.ConcurrentHashMap;
3028
import java.util.function.Supplier;
3129

3230
/**
33-
* Cache implementation of the {@link CodecFinder}. This will keep the relevant {@link Codec} for the type, format and database type cached for faster access.
34-
* In case the {@link Codec} can't be found in the cache, a fallback search using {@link CodecFinderDefaultImpl} will be done.
31+
* Cache implementation of the {@link CodecLookup}. This will keep the relevant {@link Codec} for the type, format and database type cached for faster access.
32+
* In case the {@link Codec} can't be found in the cache, a fallback search using {@link DefaultCodecLookup} will be done.
33+
*
34+
* @since 0.8.9
3535
*/
36-
public class CodecFinderCacheImpl implements CodecFinder {
37-
38-
private static final Logger LOG = Loggers.getLogger(CodecFinderCacheImpl.class);
36+
class CachedCodecLookup implements CodecLookup {
3937

40-
List<Codec<?>> codecs = Collections.emptyList();
38+
private static final Logger LOG = Loggers.getLogger(CachedCodecLookup.class);
4139

4240
private final Map<Integer, Codec<?>> decodeCodecsCache = new ConcurrentHashMap<>();
4341

4442
private final Map<Integer, Codec<?>> encodeCodecsCache = new ConcurrentHashMap<>();
4543

4644
private final Map<Integer, Codec<?>> encodeNullCodecsCache = new ConcurrentHashMap<>();
4745

48-
private final CodecFinder fallBackFinder;
46+
private final CodecLookup delegate;
4947

50-
public CodecFinderCacheImpl() {
51-
this(new CodecFinderDefaultImpl());
48+
public CachedCodecLookup(Iterable<Codec<?>> codecRegistry) {
49+
this.delegate = new DefaultCodecLookup(codecRegistry);
5250
}
5351

54-
public CodecFinderCacheImpl(CodecFinder fallBackFinder) {
55-
Assert.isTrue(!(fallBackFinder instanceof CodecFinderCacheImpl), "fallBackFinder must not be of type CodecFinderCacheImpl");
56-
this.fallBackFinder = fallBackFinder;
52+
public CachedCodecLookup(CodecLookup delegate) {
53+
Assert.isTrue(!(delegate instanceof CachedCodecLookup), "delegate must not be of type CachedCodecLookup");
54+
this.delegate = delegate;
5755
}
5856

59-
private static <T> int generateCodecHash(int dataType, Format format, Class<? extends T> type) {
60-
int hash = (dataType << 5) - dataType;
61-
hash = (hash << 5) - hash + format.hashCode();
62-
hash = (hash << 5) - hash + generateCodecHash(type);
63-
return hash;
57+
@Override
58+
public Iterator<Codec<?>> iterator() {
59+
return this.delegate.iterator();
6460
}
6561

66-
private static <T> int generateCodecHash(Class<? extends T> type) {
67-
int hash = type.hashCode();
68-
if (type.getComponentType() != null) {
69-
hash = (hash << 5) - hash + generateCodecHash(type.getComponentType());
70-
}
71-
return hash;
72-
}
62+
@Override
63+
public void afterCodecAdded() {
7364

74-
void invalidateCaches() {
7565
this.decodeCodecsCache.clear();
7666
this.encodeCodecsCache.clear();
7767
this.encodeNullCodecsCache.clear();
78-
buildCaches();
79-
}
8068

81-
void buildCaches() {
82-
for (Codec<?> c : this.codecs) {
83-
cacheEncode(c, c.type());
84-
for (PostgresqlObjectId identifier : c.getDataTypes()) {
85-
for (Format format : c.getFormats()) {
86-
cacheDecode(c, c.type(), identifier, format);
69+
for (Codec<?> c : this.delegate) {
70+
71+
if (c instanceof CodecMetadata) {
72+
CodecMetadata metadata = (CodecMetadata) c;
73+
74+
cacheEncode(c, metadata.type());
75+
for (PostgresqlObjectId identifier : metadata.getDataTypes()) {
76+
for (Format format : metadata.getFormats()) {
77+
cacheDecode(c, metadata.type(), identifier, format);
78+
}
8779
}
8880
}
8981
}
9082
// Handle decode to Object.class support
9183
for (PostgresqlObjectId identifier : PostgresqlObjectId.values()) {
9284
for (Format format : Format.all()) {
93-
Codec<?> c = fallBackFinder.findDecodeCodec(identifier.getObjectId(), format, Object.class);
85+
Codec<?> c = this.delegate.findDecodeCodec(identifier.getObjectId(), format, Object.class);
9486
if (c != null) {
9587
cacheDecode(c, Object.class, identifier, format);
9688
}
9789
}
9890
}
9991
}
10092

101-
private void cacheDecode(Codec<?> c, Class<?> type, PostgresqlObjectId identifier, Format format) {
102-
int decodeHash = generateCodecHash(identifier.getObjectId(), format, type);
103-
decodeCodecsCache.putIfAbsent(decodeHash, c);
104-
}
105-
106-
private void cacheEncode(Codec<?> c, Class<?> type) {
107-
int encodeHash = generateCodecHash(type);
108-
encodeCodecsCache.putIfAbsent(encodeHash, c);
109-
if (c.canEncodeNull(type)) {
110-
encodeNullCodecsCache.putIfAbsent(encodeHash, c);
111-
}
112-
}
113-
114-
@SuppressWarnings("unchecked")
115-
synchronized <T> Codec<T> findCodec(int codecHash, Map<Integer, Codec<?>> cache, Supplier<Codec<T>> fallback) {
116-
return Optional.ofNullable((Codec<T>) cache.get(codecHash)).orElseGet(fallback);
117-
}
118-
119-
@Override
120-
public synchronized void updateCodecs(List<Codec<?>> codecs) {
121-
this.codecs = codecs;
122-
fallBackFinder.updateCodecs(codecs);
123-
invalidateCaches();
124-
}
125-
12693
@Override
12794
public <T> Codec<T> findDecodeCodec(int dataType, Format format, Class<? extends T> type) {
128-
int hash = generateCodecHash(dataType, format, type);
129-
return findCodec(hash, decodeCodecsCache, () -> {
95+
Integer hash = generateCodecHash(dataType, format, type);
96+
return findCodec(hash, this.decodeCodecsCache, () -> {
13097
LOG.trace("[codec-finder dataType={}, format={}, type={}] Decode codec not found in cache", dataType, format, type.getName());
131-
Codec<T> c = fallBackFinder.findDecodeCodec(dataType, format, type);
98+
Codec<T> c = this.delegate.findDecodeCodec(dataType, format, type);
13299
if (c != null) {
133-
decodeCodecsCache.putIfAbsent(hash, c);
100+
this.decodeCodecsCache.putIfAbsent(hash, c);
134101
}
135102
return c;
136103
});
137104
}
138105

139106
@Override
140107
public <T> Codec<T> findEncodeCodec(T value) {
141-
int hash = generateCodecHash(value.getClass());
142-
return findCodec(hash, encodeCodecsCache, () -> {
108+
Integer hash = generateCodecHash(value.getClass());
109+
return findCodec(hash, this.encodeCodecsCache, () -> {
143110
LOG.trace("[codec-finder type={}] Encode codec not found in cache", value.getClass().getName());
144-
Codec<T> c = fallBackFinder.findEncodeCodec(value);
111+
Codec<T> c = this.delegate.findEncodeCodec(value);
145112
if (c != null) {
146-
encodeCodecsCache.putIfAbsent(hash, c);
113+
this.encodeCodecsCache.putIfAbsent(hash, c);
147114
}
148115
return c;
149116
});
150117
}
151118

152119
@Override
153120
public <T> Codec<T> findEncodeNullCodec(Class<T> type) {
154-
int hash = generateCodecHash(type);
155-
return findCodec(hash, encodeNullCodecsCache, () -> {
121+
Integer hash = generateCodecHash(type);
122+
return findCodec(hash, this.encodeNullCodecsCache, () -> {
156123
LOG.trace("[codec-finder type={}] Encode null codec not found in cache", type.getName());
157-
Codec<T> c = fallBackFinder.findEncodeNullCodec(type);
124+
Codec<T> c = this.delegate.findEncodeNullCodec(type);
158125
if (c != null) {
159-
encodeNullCodecsCache.putIfAbsent(hash, c);
126+
this.encodeNullCodecsCache.putIfAbsent(hash, c);
160127
}
161128
return c;
162129
});
163130
}
164131

132+
private void cacheDecode(Codec<?> c, Class<?> type, PostgresqlObjectId identifier, Format format) {
133+
Integer decodeHash = generateCodecHash(identifier.getObjectId(), format, type);
134+
this.decodeCodecsCache.putIfAbsent(decodeHash, c);
135+
}
136+
137+
private void cacheEncode(Codec<?> c, Class<?> type) {
138+
Integer encodeHash = generateCodecHash(type);
139+
this.encodeCodecsCache.putIfAbsent(encodeHash, c);
140+
if (c.canEncodeNull(type)) {
141+
this.encodeNullCodecsCache.putIfAbsent(encodeHash, c);
142+
}
143+
}
144+
145+
@SuppressWarnings("unchecked")
146+
private synchronized <T> Codec<T> findCodec(Integer codecHash, Map<Integer, Codec<?>> cache, Supplier<Codec<T>> fallback) {
147+
Codec<T> value = (Codec<T>) cache.get(codecHash);
148+
return value != null ? value : fallback.get();
149+
}
150+
151+
private static Integer generateCodecHash(int dataType, Format format, Class<?> type) {
152+
int hash = (dataType << 5) - dataType;
153+
hash = (hash << 5) - hash + format.hashCode();
154+
hash = (hash << 5) - hash + generateCodecHash(type);
155+
return hash;
156+
}
157+
158+
private static Integer generateCodecHash(Class<?> type) {
159+
int hash = type.hashCode();
160+
if (type.getComponentType() != null) {
161+
hash = (hash << 5) - hash + generateCodecHash(type.getComponentType());
162+
}
163+
return hash;
164+
}
165+
165166
}

src/main/java/io/r2dbc/postgresql/codec/ClobCodec.java

-5
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,4 @@ public Iterable<PostgresqlObjectId> getDataTypes() {
8282
return Arrays.asList(VARCHAR, TEXT);
8383
}
8484

85-
@Override
86-
public Iterable<Format> getFormats() {
87-
return Arrays.asList(FORMAT_TEXT, FORMAT_BINARY);
88-
}
89-
9085
}

src/main/java/io/r2dbc/postgresql/codec/Codec.java

-21
Original file line numberDiff line numberDiff line change
@@ -88,25 +88,4 @@ public interface Codec<T> {
8888
*/
8989
Parameter encodeNull();
9090

91-
/**
92-
* Returns the Java {@link Class type} of this codec.
93-
*
94-
* @return the Java type
95-
*/
96-
Class<?> type();
97-
98-
/**
99-
* Returns the collection of {@link Format} supported by this codec
100-
*
101-
* @return the formats
102-
*/
103-
Iterable<Format> getFormats();
104-
105-
/**
106-
* Returns the collection of {@link PostgresqlObjectId} this codec can handle
107-
*
108-
* @return the datatypes
109-
*/
110-
Iterable<PostgresqlObjectId> getDataTypes();
111-
11291
}

src/main/java/io/r2dbc/postgresql/codec/CodecFinder.java renamed to src/main/java/io/r2dbc/postgresql/codec/CodecLookup.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@
1919
import io.r2dbc.postgresql.message.Format;
2020
import reactor.util.annotation.Nullable;
2121

22-
import java.util.List;
23-
2422
/**
25-
* Helper used to find a {@link Codec} for encoding or decoding actions from a provided list of codecs.
23+
* Helper used to find a {@link Codec} for encoding or decoding actions from a provided collection of codecs such as a {@link CodecRegistry}.
24+
*
25+
* @since 0.8.9
2626
*/
27-
public interface CodecFinder {
28-
29-
void updateCodecs(List<Codec<?>> codecs);
27+
interface CodecLookup extends Iterable<Codec<?>> {
3028

3129
@Nullable
3230
<T> Codec<T> findDecodeCodec(int dataType, Format format, Class<? extends T> type);
@@ -37,4 +35,11 @@ public interface CodecFinder {
3735
@Nullable
3836
<T> Codec<T> findEncodeNullCodec(Class<T> type);
3937

38+
/**
39+
* Hook called after a codec was added.
40+
*/
41+
default void afterCodecAdded() {
42+
43+
}
44+
4045
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2021 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+
17+
package io.r2dbc.postgresql.codec;
18+
19+
import io.r2dbc.postgresql.message.Format;
20+
import io.r2dbc.postgresql.type.PostgresqlObjectId;
21+
22+
/**
23+
* Metadata for a codec.
24+
* <p>Codecs implementing this interface expose their supported {@link Format formats} and {@link #getDataTypes() data types}. This metadata can be used for cache warming of the actual codec lookup.
25+
*
26+
* @see PostgresqlObjectId
27+
* @see Format
28+
* @see Codec
29+
* @since 0.8.9
30+
*/
31+
public interface CodecMetadata {
32+
33+
/**
34+
* Returns the Java {@link Class type} of this codec.
35+
*
36+
* @return the Java type
37+
*/
38+
Class<?> type();
39+
40+
/**
41+
* Returns the collection of {@link Format} supported by this codec
42+
*
43+
* @return the formats
44+
*/
45+
default Iterable<Format> getFormats() {
46+
return Format.all();
47+
}
48+
49+
/**
50+
* Returns the collection of {@link PostgresqlObjectId} this codec can handle
51+
*
52+
* @return the datatypes
53+
*/
54+
Iterable<PostgresqlObjectId> getDataTypes();
55+
56+
}

0 commit comments

Comments
 (0)