RFR: 8345139: Fix bugs and inconsistencies in the Provider services map [v5]

Valerie Peng valeriep at openjdk.org
Tue Mar 4 18:34:07 UTC 2025


On Wed, 12 Feb 2025 20:46:31 GMT, Francisco Ferrari Bihurriet <fferrari at openjdk.org> wrote:

>> Hi, this pull request implements the fixes for bugs and inconsistencies described in [JDK-8345139](https://bugs.openjdk.org/browse/JDK-8345139 "Fix bugs and inconsistencies in the Provider services map").
>> 
>> #### New services map design
>> 
>> Here is the high-level hierarchy of the new services map design:
>> 
>> * `servicesMap` (`ServicesMap`)
>>     * Instances (fields)
>>         * `services` (`Map<ServiceKey, Service>`): unifies the previous `serviceMap` and `legacyMap`
>>         * `legacySvcKeys` (`Set<ServiceKey>`): set indicating which keys in `services` correspond to the Legacy API
>>         * `serviceProps` (`Map<ServiceKey, String>`): keeps track of the _provider Hashtable entries_ that originated services entries <sup>(1)</sup>
>>         * `serviceAttrProps` (`Map<ServiceKey, Map<UString, String>>`): keeps track of the _provider Hashtable entries_ that originated service attributes <sup>(1)</sup>
>>         * `serviceSet` (`AtomicReference<Set<Service>>`): part of a lock-free mechanism to implement a consistent version of the `getServices()` readers method
>>     * Writers' methods (for providers registering services through the Current or the Legacy API)
>>         * `boolean putService(Service svc)`
>>         * `boolean removeService(Service svc)`
>>         * `boolean putClassNameLegacy(ServiceKey key, String className, String propKey)`
>>         * `boolean putAliasLegacy(ServiceKey key, ServiceKey aliasKey, String propKey)`
>>         * `boolean putAttributeLegacy(ServiceKey key, String attrName, String attrValue, String propKey)`
>>         * `boolean removeLegacy(ServiceKey key, String className)`
>>         * `boolean removeAliasLegacy(ServiceKey key, ServiceKey aliasKey)`
>>         * `boolean removeAttributeLegacy(ServiceKey key, String attrName, String attrValue)`
>>     * Readers' methods (for services users and `GetInstance` APIs)
>>         * `Set<Service> getServices()`
>>         * `Service getService(ServiceKey key)`
>>     * Other methods: default and copy constructors, `void clear()`
>> 
>> (1): to maintain the consistency between the provider's `servicesMap` and its _Hashtable entries_, even if Legacy API updates occur through _properties_ with different casing, or aliases instead of main algorithms.
>> 
>> #### Testing
>> 
>> As part of our testing, we observed all the tests pass in the following categories:
>> 
>> * `jdk:tier1` (see [GitHub Actions run](https://github.com/franferrax/jdk/actions/runs/12193211398))
>> * `jdk/com/sun/crypto`
>> * `jd...
>
> Francisco Ferrari Bihurriet has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Clear ServicesMap fields in the declared order
>   
>   Constructors assign the fields in the same order.

src/java.base/share/classes/java/security/Provider.java line 637:

> 635:     // let javadoc show doc from superclass
> 636:     @Override
> 637:     public synchronized Object get(Object key) {

How about the getProperty(String) method on line 675? Add `@Override` tag and `synchronized` keyword there also? And the `keySet()` and `values()` methods on line 432 and 444 respectively? What is the criteria for synchronizing the method of the `Provider` class?

src/java.base/share/classes/java/security/Provider.java line 713:

> 711:          */
> 712:         private record MappingInfo(Service svc, ServiceKey algKey,
> 713:                 Boolean isLegacy) {}

There is comment states that `isLegacy` may be null, but then I also see a few if-cond using `isLegacy` directly like its a boolean, wouldn't it lead to NPE if `isLegacy` is `null`?

src/java.base/share/classes/java/security/Provider.java line 735:

> 733:         // with the Legacy API. The absence of a service key on this set is an
> 734:         // indication that the service was either not added or added with the
> 735:         // Current API. Only algorithm service keys are added to this set.

nit: I find the sentence "The absence of a service key on this set ... added with Current API" is somewhat redundant. I suppose you mean "not added with the Legacy API or added with the Current API". The first sentence is clear enough and the second sentence doesn't add much value.

src/java.base/share/classes/java/security/Provider.java line 750:

> 748:         private final Map<ServiceKey, Map<UString, String>> serviceAttrProps;
> 749: 
> 750:         ServicesMap() {

nit: add comment for this constructor?

src/java.base/share/classes/java/security/Provider.java line 988:

> 986:                         // The service was added with the Current API. Overwrite
> 987:                         // the alias entry on the services map without modifying
> 988:                         // the service that is currently using it.

Is the "service" in the above line really means the provider `service` entry? If so, may be "associated with" is better than "using". Also there is no code under this comment block, where is the action of "overwrite the alias entry on the services map"?

src/java.base/share/classes/java/security/Provider.java line 1037:

> 1035:             }
> 1036: 
> 1037:             if (mi.isLegacy) {

For legacy entry, there is no check on the `legacyApiCall` value, is this due to the call path from `resolveKeyConflict` method? However, should a legacy entry be removed by the `removeService` method? If not, then additional check may be needed?

src/java.base/share/classes/java/security/Provider.java line 1654:

> 1652:     }
> 1653: 
> 1654:     // used as key in the serviceMap and legacyMap HashMaps

This comment is obsolete with the new impl and should be updated.

src/java.base/share/classes/java/security/Provider.java line 2071:

> 2069:         // For services added to a ServicesMap, their algorithm service key.
> 2070:         // This value derives from the algorithm field. For services (still)
> 2071:         // not added to a ServicesMap, value is null.

The current comment is a bit hard to read.
How about "The mapping key of this service when added to a `ServiceMap`; null if not yet added to a `ServiceMap`"?
The value is derived from the type and algorithm and this is straightforward enough that probably need not be commented.

src/java.base/share/classes/java/security/Provider.java line 2078:

> 2076:         // entries derive from the aliases field, keys are not repeated
> 2077:         // (case-insensitive comparison) and not equal to the algorithm. For
> 2078:         // services (still) not added to a ServicesMap, value is an empty map.

Could we re-write it to summarize the conditions for empty map? It could be easier to read/understand.
For example: empty map if no aliases or if this service is not yet added to a `ServiceMap`.
The part of case-insensitive comparision, it's due to the impl of `ServiceKey`, right? Maybe we can simply refer to that no need to describe it here.

src/java.base/share/classes/java/security/Provider.java line 2110:

> 2108: 
> 2109:         /*
> 2110:          * Constructor used from the ServicesMap Legacy API.

nit: "used **by**"? Same goes for other places.

src/java.base/share/classes/java/security/Provider.java line 2149:

> 2147:                 attributes = new HashMap<>(svc.attributes);
> 2148:             }
> 2149:             registered = false;

I didn't see it's set to `true` in any of the constructors; also the default value is already `false`, why only explicitly set it to `false` here?

src/java.base/share/classes/java/security/Provider.java line 2158:

> 2156:             hasKeyAttributes = null;
> 2157:             supportedFormats = null;
> 2158:             supportedClasses = null;

Are these necessary? The other constructor didn't set them.

src/java.base/share/classes/java/security/Provider.java line 2202:

> 2200:                     "Attribute value expected to exist with the same identity.";
> 2201:             attributes.remove(attrKey, attrValue);
> 2202:         }

Is the new impl assuming `attrValue` should never be `null`? Based on javadoc of `Map.remove(Object key, Object value)`, the new impl removes the entry when the associated value is `null` vs the original impl removes the entry regardless of the associated value.

src/java.base/share/classes/java/security/Provider.java line 2241:

> 2239:                 this.attributes = Collections.emptyMap();
> 2240:             } else {
> 2241:                 this.attributes = new HashMap<>();

Initialize with `attributes.size()` and load factor 1.0 if both are the same size?

src/java.base/share/classes/java/security/Provider.java line 2256:

> 2254:          * keys with Service::addAliasKey.
> 2255:          */
> 2256:         private void generateServiceKeys() {

nit: move this private method to be with other private methods?

src/java.base/share/classes/java/security/Provider.java line 2268:

> 2266:                     }
> 2267:                 }
> 2268:                 aliasKeys = Collections.unmodifiableMap(aliasKeys);

if `aliases` are empty, then we can skip line 2261-2268 and no need to update `aliasKeys`?

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1977987224
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1978378764
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970776267
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970784237
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1978485726
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1978488877
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970753720
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970723732
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970720272
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970724166
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970437584
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970428109
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970698200
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970710298
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970740763
PR Review Comment: https://git.openjdk.org/jdk/pull/22613#discussion_r1970736998


More information about the security-dev mailing list