Skip to content

Commit bdd9e0d

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 pgjdbc#410][pgjdbc#409] Signed-off-by: Mark Paluch <[email protected]>
1 parent 86a8fb5 commit bdd9e0d

19 files changed

+461
-411
lines changed

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,17 @@
2525
import reactor.core.publisher.Mono;
2626
import reactor.util.annotation.Nullable;
2727

28-
import java.util.EnumSet;
2928
import java.util.function.Supplier;
3029

3130
import static io.r2dbc.postgresql.client.EncodedParameter.NULL_VALUE;
32-
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
33-
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
3431

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

4340
private final Class<T> type;
4441

@@ -113,12 +110,6 @@ public Class<?> type() {
113110
return this.type;
114111
}
115112

116-
@Override
117-
public Iterable<Format> getFormats() {
118-
// Unless overridden all codecs supports both text and binary format
119-
return EnumSet.of(FORMAT_TEXT, FORMAT_BINARY);
120-
}
121-
122113
/**
123114
* Create a {@link EncodedParameter}.
124115
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*
2626
* @since 0.8.8
2727
*/
28-
interface ArrayCodecDelegate<T> {
28+
interface ArrayCodecDelegate<T> extends CodecMetadata {
2929

3030
/**
3131
* Encode the {@code value} to be used as parameter to be used as array element.

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

Lines changed: 76 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -22,151 +22,152 @@
2222
import reactor.util.Loggers;
2323

2424
import java.lang.reflect.Array;
25-
import java.util.Collections;
26-
import java.util.List;
25+
import java.util.Iterator;
2726
import java.util.Map;
2827
import java.util.Optional;
2928
import java.util.concurrent.ConcurrentHashMap;
3029
import java.util.function.Supplier;
3130

3231
/**
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.
32+
* Cache implementation of the {@link CodecLookup}. This will keep the relevant {@link Codec} for the type, format and database type cached for faster access.
33+
* In case the {@link Codec} can't be found in the cache, a fallback search using {@link DefaultCodecLookup} will be done.
34+
*
35+
* @since 0.9
3536
*/
36-
public class CodecFinderCacheImpl implements CodecFinder {
37-
38-
private static final Logger LOG = Loggers.getLogger(CodecFinderCacheImpl.class);
37+
class CachedCodecLookup implements CodecLookup {
3938

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

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

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

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

48-
private final CodecFinder fallBackFinder;
47+
private final CodecLookup delegate;
4948

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

54-
public CodecFinderCacheImpl(CodecFinder fallBackFinder) {
55-
Assert.requireNonType(fallBackFinder, CodecFinderCacheImpl.class, "fallBackFinder must not be of type CodecFinderCacheImpl");
56-
this.fallBackFinder = fallBackFinder;
53+
public CachedCodecLookup(CodecLookup delegate) {
54+
Assert.requireNonType(delegate, CachedCodecLookup.class, "delegate must not be of type CodecFinderCacheImpl");
55+
this.delegate = delegate;
5756
}
5857

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;
58+
@Override
59+
public Iterator<Codec<?>> iterator() {
60+
return this.delegate.iterator();
6461
}
6562

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-
}
63+
@Override
64+
public void afterCodecAdded() {
7365

74-
void invalidateCaches() {
7566
this.decodeCodecsCache.clear();
7667
this.encodeCodecsCache.clear();
7768
this.encodeNullCodecsCache.clear();
78-
buildCaches();
79-
}
8069

81-
void buildCaches() {
82-
for (Codec<?> c : this.codecs) {
70+
for (Codec<?> c : this.delegate) {
8371
Optional<Class<?>> arrayClass = Optional.empty();
8472
if (c instanceof ArrayCodec) {
8573
ArrayCodec<?> ac = (ArrayCodec<?>) c;
8674
arrayClass = Optional.of(Array.newInstance(ac.getComponentType(), 0).getClass());
8775
}
88-
cacheEncode(c, c.type());
89-
arrayClass.ifPresent(ac -> cacheEncode(c, ac));
90-
for (PostgresTypeIdentifier identifier : c.getDataTypes()) {
91-
for (Format format : c.getFormats()) {
92-
cacheDecode(c, c.type(), identifier, format);
93-
arrayClass.ifPresent(ac -> cacheDecode(c, ac, identifier, format));
76+
77+
if (c instanceof CodecMetadata) {
78+
CodecMetadata metadata = (CodecMetadata) c;
79+
cacheEncode(c, metadata.type());
80+
arrayClass.ifPresent(ac -> cacheEncode(c, ac));
81+
for (PostgresTypeIdentifier identifier : metadata.getDataTypes()) {
82+
for (Format format : metadata.getFormats()) {
83+
cacheDecode(c, metadata.type(), identifier, format);
84+
arrayClass.ifPresent(ac -> cacheDecode(c, ac, identifier, format));
85+
}
9486
}
9587
}
9688
}
9789
// Handle decode to Object.class support
9890
for (PostgresqlObjectId identifier : PostgresqlObjectId.values()) {
9991
for (Format format : Format.all()) {
100-
Codec<?> c = fallBackFinder.findDecodeCodec(identifier.getObjectId(), format, Object.class);
92+
Codec<?> c = this.delegate.findDecodeCodec(identifier.getObjectId(), format, Object.class);
10193
if (c != null) {
10294
cacheDecode(c, Object.class, identifier, format);
10395
}
10496
}
10597
}
10698
}
10799

108-
private void cacheDecode(Codec<?> c, Class<?> type, PostgresTypeIdentifier identifier, Format format) {
109-
int decodeHash = generateCodecHash(identifier.getObjectId(), format, type);
110-
decodeCodecsCache.putIfAbsent(decodeHash, c);
111-
}
112-
113-
private void cacheEncode(Codec<?> c, Class<?> type) {
114-
int encodeHash = generateCodecHash(type);
115-
encodeCodecsCache.putIfAbsent(encodeHash, c);
116-
if (c.canEncodeNull(type)) {
117-
encodeNullCodecsCache.putIfAbsent(encodeHash, c);
118-
}
119-
}
120-
121-
@SuppressWarnings("unchecked")
122-
synchronized <T> Codec<T> findCodec(int codecHash, Map<Integer, Codec<?>> cache, Supplier<Codec<T>> fallback) {
123-
return Optional.ofNullable((Codec<T>) cache.get(codecHash)).orElseGet(fallback);
124-
}
125-
126-
@Override
127-
public synchronized void updateCodecs(List<Codec<?>> codecs) {
128-
this.codecs = codecs;
129-
fallBackFinder.updateCodecs(codecs);
130-
invalidateCaches();
131-
}
132-
133100
@Override
134101
public <T> Codec<T> findDecodeCodec(int dataType, Format format, Class<? extends T> type) {
135-
int hash = generateCodecHash(dataType, format, type);
136-
return findCodec(hash, decodeCodecsCache, () -> {
102+
Integer hash = generateCodecHash(dataType, format, type);
103+
return findCodec(hash, this.decodeCodecsCache, () -> {
137104
LOG.trace("[codec-finder dataType={}, format={}, type={}] Decode codec not found in cache", dataType, format, type.getName());
138-
Codec<T> c = fallBackFinder.findDecodeCodec(dataType, format, type);
105+
Codec<T> c = this.delegate.findDecodeCodec(dataType, format, type);
139106
if (c != null) {
140-
decodeCodecsCache.putIfAbsent(hash, c);
107+
this.decodeCodecsCache.putIfAbsent(hash, c);
141108
}
142109
return c;
143110
});
144111
}
145112

146113
@Override
147114
public <T> Codec<T> findEncodeCodec(T value) {
148-
int hash = generateCodecHash(value.getClass());
149-
return findCodec(hash, encodeCodecsCache, () -> {
115+
Integer hash = generateCodecHash(value.getClass());
116+
return findCodec(hash, this.encodeCodecsCache, () -> {
150117
LOG.trace("[codec-finder type={}] Encode codec not found in cache", value.getClass().getName());
151-
Codec<T> c = fallBackFinder.findEncodeCodec(value);
118+
Codec<T> c = this.delegate.findEncodeCodec(value);
152119
if (c != null) {
153-
encodeCodecsCache.putIfAbsent(hash, c);
120+
this.encodeCodecsCache.putIfAbsent(hash, c);
154121
}
155122
return c;
156123
});
157124
}
158125

159126
@Override
160127
public <T> Codec<T> findEncodeNullCodec(Class<T> type) {
161-
int hash = generateCodecHash(type);
162-
return findCodec(hash, encodeNullCodecsCache, () -> {
128+
Integer hash = generateCodecHash(type);
129+
return findCodec(hash, this.encodeNullCodecsCache, () -> {
163130
LOG.trace("[codec-finder type={}] Encode null codec not found in cache", type.getName());
164-
Codec<T> c = fallBackFinder.findEncodeNullCodec(type);
131+
Codec<T> c = this.delegate.findEncodeNullCodec(type);
165132
if (c != null) {
166-
encodeNullCodecsCache.putIfAbsent(hash, c);
133+
this.encodeNullCodecsCache.putIfAbsent(hash, c);
167134
}
168135
return c;
169136
});
170137
}
171138

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

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.TEXT;
3333
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.VARCHAR;
34-
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
3534
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
3635

3736
final class ClobCodec extends AbstractCodec<Clob> {
@@ -86,9 +85,4 @@ public Iterable<PostgresTypeIdentifier> getDataTypes() {
8685
return Arrays.asList(VARCHAR, TEXT);
8786
}
8887

89-
@Override
90-
public Iterable<Format> getFormats() {
91-
return Arrays.asList(FORMAT_TEXT, FORMAT_BINARY);
92-
}
93-
9488
}

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,4 @@ public interface Codec<T> {
9797
*/
9898
EncodedParameter encodeNull();
9999

100-
/**
101-
* Returns the Java {@link Class type} of this codec.
102-
*
103-
* @return the Java type
104-
*/
105-
Class<?> type();
106-
107-
/**
108-
* Returns the collection of {@link Format} supported by this codec
109-
*
110-
* @return the formats
111-
*/
112-
Iterable<Format> getFormats();
113-
114-
/**
115-
* Returns the collection of {@link PostgresTypeIdentifier} this codec can handle
116-
*
117-
* @return the datatypes
118-
*/
119-
Iterable<PostgresTypeIdentifier> getDataTypes();
120-
121100
}

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

Lines changed: 11 additions & 6 deletions
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.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
}

0 commit comments

Comments
 (0)