Skip to content

Commit 97e0fd0

Browse files
committed
Deadlock when many threads invoke CodeList.valueOf(Class, String, Function) for the same class in same time.
This is caused by the lock on the `WeakHashMap` which can be taken before the class initialization is finished. This bug was introduced by #91 The fix is to simplify `CodeList` by removing the `WeakHashMap`. Instead, a `VALUES` static field is declared in each `CodeList` subclass. This is a more traditional approach as the cost of being more cumbersome for subclasses. The advantage is that the list of values can not longer be accessed before class initialization is finished. As a side effect, garbage collection of classes may work better because we no longer keep references to them. The `Vocabulary` internal annotation is no longer needed after this change and is removed.
1 parent 19c195b commit 97e0fd0

64 files changed

Lines changed: 1942 additions & 1268 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

geoapi-conformance/src/main/java/org/opengis/test/Configuration.java

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.opengis.test;
1919

2020
import java.util.Map;
21+
import java.util.List;
2122
import java.util.Collections;
2223
import java.util.LinkedHashMap;
2324
import java.util.Objects;
@@ -251,8 +252,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
251252
*
252253
* @see org.opengis.test.util.NameTest#isMultiLocaleSupported
253254
*/
254-
public static final Key<Boolean> isMultiLocaleSupported =
255-
new Key<>(Boolean.class, "isMultiLocaleSupported");
255+
public static final Key<Boolean> isMultiLocaleSupported;
256256

257257
/**
258258
* Whether the {@link GenericName} instances can apply different syntax rules in different
@@ -264,35 +264,30 @@ public static final class Key<T> extends CodeList<Key<?>> {
264264
*
265265
* @see org.opengis.test.util.NameTest#isMixedNameSyntaxSupported
266266
*/
267-
public static final Key<Boolean> isMixedNameSyntaxSupported =
268-
new Key<>(Boolean.class, "isMixedNameSyntaxSupported");
267+
public static final Key<Boolean> isMixedNameSyntaxSupported;
269268

270269
/**
271270
* Whether the {@link IdentifiedObject} instances have {@linkplain IdentifiedObject#getName()
272271
* names} matching the names declared in the EPSG database.
273272
*/
274-
public static final Key<Boolean> isStandardNameSupported =
275-
new Key<>(Boolean.class, "isStandardNameSupported");
273+
public static final Key<Boolean> isStandardNameSupported;
276274

277275
/**
278276
* Whether the {@link IdentifiedObject} instances have at least the
279277
* {@linkplain IdentifiedObject#getAlias() aliases} declared in the EPSG database.
280278
*/
281-
public static final Key<Boolean> isStandardAliasSupported =
282-
new Key<>(Boolean.class, "isStandardAliasSupported");
279+
public static final Key<Boolean> isStandardAliasSupported;
283280

284281
/**
285282
* Whether the {@link IdentifiedObject} instances created indirectly by the factories
286283
* are expected to have correct identification information.
287284
*/
288-
public static final Key<Boolean> isDependencyIdentificationSupported =
289-
new Key<>(Boolean.class, "isDependencyIdentificationSupported");
285+
public static final Key<Boolean> isDependencyIdentificationSupported;
290286

291287
/**
292288
* Whether the authority factory supports creation of deprecated {@link IdentifiedObject} instances.
293289
*/
294-
public static final Key<Boolean> isDeprecatedObjectCreationSupported =
295-
new Key<>(Boolean.class, "isDeprecatedObjectCreationSupported");
290+
public static final Key<Boolean> isDeprecatedObjectCreationSupported;
296291

297292
/**
298293
* Whether {@link MathTransform#transform(double[], int, double[], int, int)} is supported.
@@ -301,8 +296,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
301296
*
302297
* @see org.opengis.test.referencing.TransformTestCase#isDoubleToDoubleSupported
303298
*/
304-
public static final Key<Boolean> isDoubleToDoubleSupported =
305-
new Key<>(Boolean.class, "isDoubleToDoubleSupported");
299+
public static final Key<Boolean> isDoubleToDoubleSupported;
306300

307301
/**
308302
* Whether {@link MathTransform#transform(float[], int, float[], int, int)} is supported.
@@ -311,8 +305,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
311305
*
312306
* @see org.opengis.test.referencing.TransformTestCase#isFloatToFloatSupported
313307
*/
314-
public static final Key<Boolean> isFloatToFloatSupported =
315-
new Key<>(Boolean.class, "isFloatToFloatSupported");
308+
public static final Key<Boolean> isFloatToFloatSupported;
316309

317310
/**
318311
* Whether {@link MathTransform#transform(double[], int, float[], int, int)} is supported.
@@ -321,8 +314,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
321314
*
322315
* @see org.opengis.test.referencing.TransformTestCase#isDoubleToFloatSupported
323316
*/
324-
public static final Key<Boolean> isDoubleToFloatSupported =
325-
new Key<>(Boolean.class, "isDoubleToFloatSupported");
317+
public static final Key<Boolean> isDoubleToFloatSupported;
326318

327319
/**
328320
* Whether {@link MathTransform#transform(float[], int, double[], int, int)} is supported.
@@ -331,8 +323,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
331323
*
332324
* @see org.opengis.test.referencing.TransformTestCase#isFloatToDoubleSupported
333325
*/
334-
public static final Key<Boolean> isFloatToDoubleSupported =
335-
new Key<>(Boolean.class, "isFloatToDoubleSupported");
326+
public static final Key<Boolean> isFloatToDoubleSupported;
336327

337328
/**
338329
* Whether source and destination arrays can overlap in {@link MathTransform} operations.
@@ -351,8 +342,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
351342
*
352343
* @see org.opengis.test.referencing.TransformTestCase#isOverlappingArraySupported
353344
*/
354-
public static final Key<Boolean> isOverlappingArraySupported =
355-
new Key<>(Boolean.class, "isOverlappingArraySupported");
345+
public static final Key<Boolean> isOverlappingArraySupported;
356346

357347
/**
358348
* Whether {@link MathTransform#inverse()} is supported.
@@ -361,8 +351,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
361351
*
362352
* @see org.opengis.test.referencing.TransformTestCase#isInverseTransformSupported
363353
*/
364-
public static final Key<Boolean> isInverseTransformSupported =
365-
new Key<>(Boolean.class, "isInverseTransformSupported");
354+
public static final Key<Boolean> isInverseTransformSupported;
366355

367356
/**
368357
* Whether {@link MathTransform#derivative(DirectPosition)} is supported.
@@ -371,16 +360,14 @@ public static final class Key<T> extends CodeList<Key<?>> {
371360
*
372361
* @see org.opengis.test.referencing.TransformTestCase#isDerivativeSupported
373362
*/
374-
public static final Key<Boolean> isDerivativeSupported =
375-
new Key<>(Boolean.class, "isDerivativeSupported");
363+
public static final Key<Boolean> isDerivativeSupported;
376364

377365
/**
378366
* Whether {@link MathTransformFactory#createAffineTransform(Matrix)} accepts non-square matrixes.
379367
*
380368
* @see org.opengis.test.referencing.AffineTransformTest#isNonSquareMatrixSupported
381369
*/
382-
public static final Key<Boolean> isNonSquareMatrixSupported =
383-
new Key<>(Boolean.class, "isNonSquareMatrixSupported");
370+
public static final Key<Boolean> isNonSquareMatrixSupported;
384371

385372
/**
386373
* Whether {@link MathTransformFactory} can create transforms between spaces that are
@@ -389,8 +376,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
389376
*
390377
* @see org.opengis.test.referencing.AffineTransformTest#isNonBidimensionalSpaceSupported
391378
*/
392-
public static final Key<Boolean> isNonBidimensionalSpaceSupported =
393-
new Key<>(Boolean.class, "isNonBidimensionalSpaceSupported");
379+
public static final Key<Boolean> isNonBidimensionalSpaceSupported;
394380

395381
/**
396382
* Whether (<var>y</var>,<var>x</var>) axis order is supported. This axis swapping is not
@@ -400,8 +386,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
400386
*
401387
* @see org.opengis.test.referencing.AuthorityFactoryTest#isAxisSwappingSupported
402388
*/
403-
public static final Key<Boolean> isAxisSwappingSupported =
404-
new Key<>(Boolean.class, "isAxisSwappingSupported");
389+
public static final Key<Boolean> isAxisSwappingSupported;
405390

406391
/**
407392
* Whether the test methods can invoke a <code>{@linkplain TestCase#validators validators}.validate(…)}</code>
@@ -417,46 +402,41 @@ public static final class Key<T> extends CodeList<Key<?>> {
417402
*
418403
* @see org.opengis.test.referencing.WKTParserTest#isValidationEnabled
419404
*/
420-
public static final Key<Boolean> isValidationEnabled =
421-
new Key<>(Boolean.class, "isValidationEnabled");
405+
public static final Key<Boolean> isValidationEnabled;
422406

423407
/**
424408
* Whether the tolerance threshold of a {@link org.opengis.test.referencing.TransformTestCase}
425409
* has been relaxed. This information is determined after test execution.
426410
*/
427-
public static final Key<Boolean> isToleranceRelaxed =
428-
new Key<>(Boolean.class, "isToleranceRelaxed");
411+
public static final Key<Boolean> isToleranceRelaxed;
429412

430413
/**
431414
* The provider of {@linkplain Units units} to use for tests. If this configuration hint
432415
* is not specified, then the {@linkplain Units#getDefault() default instance} is used.
433416
*/
434-
public static final Key<Units> units = new Key<>(Units.class, "units");
417+
public static final Key<Units> units;
435418

436419
/**
437420
* The {@linkplain MathTransformFactory Math Transform factory} instance used for a test.
438421
*
439422
* @see org.opengis.test.referencing.AffineTransformTest#mtFactory
440423
* @see org.opengis.test.referencing.ParameterizedTransformTest#mtFactory
441424
*/
442-
public static final Key<MathTransformFactory> mtFactory =
443-
new Key<>(MathTransformFactory.class, "mtFactory");
425+
public static final Key<MathTransformFactory> mtFactory;
444426

445427
/**
446428
* The {@linkplain CRSFactory Coordinate Reference System factory} instance used for a test.
447429
* This is used mostly for parsing of documents in <abbr>WKT</abbr> or <abbr>XML</abbr> formats.
448430
*
449431
* @see org.opengis.test.referencing.WKTParserTest#crsFactory
450432
*/
451-
public static final Key<CRSFactory> crsFactory =
452-
new Key<>(CRSFactory.class, "crsFactory");
433+
public static final Key<CRSFactory> crsFactory;
453434

454435
/**
455436
* The {@linkplain RegisterOperations register operations} instance used for a test.
456437
* Provides also factories for building CRS components from authority codes.
457438
*/
458-
public static final Key<RegisterOperations> registerOperations =
459-
new Key<>(RegisterOperations.class, "registerOperations");
439+
public static final Key<RegisterOperations> registerOperations;
460440

461441
/**
462442
* Whether the objects created by the tested {@link org.opengis.referencing.ObjectFactory} use the
@@ -475,8 +455,7 @@ public static final class Key<T> extends CodeList<Key<?>> {
475455
*
476456
* If the factory does not perform any of the above conversions, then this flag can be {@code true}.
477457
*/
478-
public static final Key<Boolean> isFactoryPreservingUserValues =
479-
new Key<>(Boolean.class, "isFactoryPreservingUserValues");
458+
public static final Key<Boolean> isFactoryPreservingUserValues;
480459

481460
/**
482461
* The set of {@link Validator} instances to use for validating objects.
@@ -488,6 +467,35 @@ public static final class Key<T> extends CodeList<Key<?>> {
488467
public static final Key<ValidatorContainer> validators =
489468
new Key<>(ValidatorContainer.class, "validators");
490469

470+
/**
471+
* All code list values created in the currently running <abbr>JVM</abbr>.
472+
*/
473+
private static final List<Key<?>> VALUES = initialValues(
474+
// Inline assignments for getting compiler error if a field is missing or duplicated.
475+
isMultiLocaleSupported = new Key<>(Boolean.class, "isMultiLocaleSupported"),
476+
isMixedNameSyntaxSupported = new Key<>(Boolean.class, "isMixedNameSyntaxSupported"),
477+
isStandardNameSupported = new Key<>(Boolean.class, "isStandardNameSupported"),
478+
isStandardAliasSupported = new Key<>(Boolean.class, "isStandardAliasSupported"),
479+
isDependencyIdentificationSupported = new Key<>(Boolean.class, "isDependencyIdentificationSupported"),
480+
isDeprecatedObjectCreationSupported = new Key<>(Boolean.class, "isDeprecatedObjectCreationSupported"),
481+
isDoubleToDoubleSupported = new Key<>(Boolean.class, "isDoubleToDoubleSupported"),
482+
isFloatToFloatSupported = new Key<>(Boolean.class, "isFloatToFloatSupported"),
483+
isDoubleToFloatSupported = new Key<>(Boolean.class, "isDoubleToFloatSupported"),
484+
isFloatToDoubleSupported = new Key<>(Boolean.class, "isFloatToDoubleSupported"),
485+
isOverlappingArraySupported = new Key<>(Boolean.class, "isOverlappingArraySupported"),
486+
isInverseTransformSupported = new Key<>(Boolean.class, "isInverseTransformSupported"),
487+
isDerivativeSupported = new Key<>(Boolean.class, "isDerivativeSupported"),
488+
isNonSquareMatrixSupported = new Key<>(Boolean.class, "isNonSquareMatrixSupported"),
489+
isNonBidimensionalSpaceSupported = new Key<>(Boolean.class, "isNonBidimensionalSpaceSupported"),
490+
isAxisSwappingSupported = new Key<>(Boolean.class, "isAxisSwappingSupported"),
491+
isValidationEnabled = new Key<>(Boolean.class, "isValidationEnabled"),
492+
isToleranceRelaxed = new Key<>(Boolean.class, "isToleranceRelaxed"),
493+
units = new Key<>(Units.class, "units"),
494+
mtFactory = new Key<>(MathTransformFactory.class, "mtFactory"),
495+
crsFactory = new Key<>(CRSFactory.class, "crsFactory"),
496+
registerOperations = new Key<>(RegisterOperations.class, "registerOperations"),
497+
isFactoryPreservingUserValues = new Key<>(Boolean.class, "isFactoryPreservingUserValues"));
498+
491499
/**
492500
* The type of values associated to this key.
493501
*/
@@ -520,7 +528,7 @@ private Key(final Class<T> type, final String name) {
520528
public static <T> Key<? extends T> valueOf(final String name, final Class<T> type) {
521529
Objects.requireNonNull(type, "type");
522530
@SuppressWarnings("rawtypes")
523-
final Key<?> key = (Key<?>) valueOf(Key.class, name, (n) -> new Key(type, n)).get();
531+
final Key<?> key = (Key<?>) valueOf(VALUES, name, (n) -> new Key(type, n));
524532
if (type.isAssignableFrom(key.type)) {
525533
return (Key<? extends T>) key;
526534
}
@@ -534,7 +542,7 @@ public static <T> Key<? extends T> valueOf(final String name, final Class<T> typ
534542
*/
535543
@SuppressWarnings("unchecked")
536544
public static Key<?>[] values() {
537-
return values(Key.class);
545+
return VALUES.toArray(Key[]::new);
538546
}
539547

540548
/**

geoapi-pending/src/main/java/org/opengis/coverage/CommonPointRule.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
*/
1818
package org.opengis.coverage;
1919

20+
import java.util.List;
2021
import java.util.Collection; // For javadoc
2122
import org.opengis.util.CodeList;
2223
import org.opengis.geometry.DirectPosition;
2324
import org.opengis.annotation.UML;
24-
import org.opengis.geoapi.internal.Vocabulary;
2525

2626
import static org.opengis.annotation.Obligation.*;
2727
import static org.opengis.annotation.Specification.*;
@@ -45,7 +45,6 @@
4545
*
4646
* @see Coverage#getCommonPointRule
4747
*/
48-
@Vocabulary(capacity=6)
4948
@UML(identifier="CV_CommonPointRule", specification=ISO_19123)
5049
public class CommonPointRule extends CodeList<CommonPointRule> {
5150
/**
@@ -57,41 +56,53 @@ public class CommonPointRule extends CodeList<CommonPointRule> {
5756
* The mean of the feature attribute values.
5857
*/
5958
@UML(identifier="average", obligation=CONDITIONAL, specification=ISO_19123)
60-
public static final CommonPointRule AVERAGE = new CommonPointRule("AVERAGE");
59+
public static final CommonPointRule AVERAGE;
6160

6261
/**
6362
* The least of the feature attribute values.
6463
*/
6564
@UML(identifier="low", obligation=CONDITIONAL, specification=ISO_19123)
66-
public static final CommonPointRule LOW = new CommonPointRule("LOW");
65+
public static final CommonPointRule LOW;
6766

6867
/**
6968
* The greatest of the feature attribute values.
7069
*/
7170
@UML(identifier="high", obligation=CONDITIONAL, specification=ISO_19123)
72-
public static final CommonPointRule HIGH = new CommonPointRule("HIGH");
71+
public static final CommonPointRule HIGH;
7372

7473
/**
7574
* All the feature attribute values that can be determined for the input direct position.
7675
*/
7776
@UML(identifier="all", obligation=CONDITIONAL, specification=ISO_19123)
78-
public static final CommonPointRule ALL = new CommonPointRule("ALL");
77+
public static final CommonPointRule ALL;
7978

8079
/**
8180
* The {@linkplain ValueSegment#getStartParameter start value} of the second
8281
* {@linkplain ValueSegment value segment}.
8382
* Applies only to segmented curve coverages.
8483
*/
8584
@UML(identifier="start", obligation=CONDITIONAL, specification=ISO_19123)
86-
public static final CommonPointRule START = new CommonPointRule("START");
85+
public static final CommonPointRule START;
8786

8887
/**
8988
* The {@linkplain ValueSegment#getEndParameter end value} of the first
9089
* {@linkplain ValueSegment value segment}.
9190
* Applies only to segmented curve coverages.
9291
*/
9392
@UML(identifier="end", obligation=CONDITIONAL, specification=ISO_19123)
94-
public static final CommonPointRule END = new CommonPointRule("END");
93+
public static final CommonPointRule END;
94+
95+
/**
96+
* All code list values created in the currently running <abbr>JVM</abbr>.
97+
*/
98+
private static final List<CommonPointRule> VALUES = initialValues(
99+
// Inline assignments for getting compiler error if a field is missing or duplicated.
100+
AVERAGE = new CommonPointRule("AVERAGE"),
101+
LOW = new CommonPointRule("LOW"),
102+
HIGH = new CommonPointRule("HIGH"),
103+
ALL = new CommonPointRule("ALL"),
104+
START = new CommonPointRule("START"),
105+
END = new CommonPointRule("END"));
95106

96107
/**
97108
* Constructs an element of the given name.
@@ -109,7 +120,7 @@ private CommonPointRule(final String name) {
109120
* @return the list of codes declared in the current JVM.
110121
*/
111122
public static CommonPointRule[] values() {
112-
return values(CommonPointRule.class);
123+
return VALUES.toArray(CommonPointRule[]::new);
113124
}
114125

115126
/**
@@ -134,6 +145,6 @@ public CommonPointRule[] family() {
134145
* @return a code matching the given name.
135146
*/
136147
public static CommonPointRule valueOf(String code) {
137-
return valueOf(CommonPointRule.class, code, CommonPointRule::new).get();
148+
return valueOf(VALUES, code, CommonPointRule::new);
138149
}
139150
}

0 commit comments

Comments
 (0)