blob: bf521605467ae5705fe5e807b967f745ee555f4e [file] [log] [blame]
/*
* Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 05/06/2015-2.7 Tomas Kraus
// - Initial API and implementation.
package org.eclipse.persistence.internal.jpa;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.persistence.platform.server.ServerPlatform;
import org.eclipse.persistence.platform.server.ServerPlatformUtils;
/**
* Partition isolated {@link HashMap}. Provides HashMap with partition isolation for {@link ServerPlatform}s
* that support partitioning. Partition isolation is transparent and keeps {@link Map} API unchanged.
*/
public final class IsolatedHashMap<K, V> implements Map<K, V> {
/** Default short enough partition ID when server does not support partitions.*/
private static final String DEFAULT_PARTITION_ID = "0";
/** Default initial capacity used to create {@link HashMap}s for individual partitions. */
private static final int DEFAULT_INITIAL_CAPACITY = 16;
/** Default load factor used to create {@link HashMap}s for individual partitions. */
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
/** Detected server platform. */
private static final ServerPlatform serverPlatform;
/** Does platform support partitions? */
private static final boolean supportPartitions;
/** Class initialization code. */
static {
String serverPlatformName = ServerPlatformUtils.detectServerPlatform(null);
serverPlatform = serverPlatformName != null
? ServerPlatformUtils.createServerPlatform(
null, serverPlatformName, IsolatedHashMap.class.getClassLoader())
: null;
// False value also handles cases when serverPlatform is null to avoid NPE.
supportPartitions = serverPlatform != null ? serverPlatform.usesPartitions() : false;
}
/**
* INTERNAL:
* Partition isolated {@link Map} factory. Provides {@link Map} separated for individual partitions.
* Factory method will return {@link HashMap} on platforms without partitions support. Slower
* {@link IsolatedHashMap} instance will be used only on platforms with partitions support.
*/
public static final <K, V>Map<K, V> newMap() {
return supportPartitions ? new IsolatedHashMap<>() : new HashMap<>();
}
/** Initial capacity used to create {@link HashMap}s for individual partitions. */
private final int initialCapacity;
/** Initial load factor used to create {@link HashMap}s for individual partitions. */
private final float loadFactor;
/** Partition ID to {@link Map} mapping. Used when platform does support partitions. */
private final Map<String, Map<K, V>> maps;
/**
* Constructs an empty {@code IsolatedHashMap} with the default initial capacity {@code 16} and the default
* load factor {@code 0.75} for every partition.
*/
private IsolatedHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs an empty {@code IsolatedHashMap} with the initial capacity and the default
* load factor specified as arguments.
* @param initialCapacity Initial capacity used to create {@link HashMap}s for individual partitions.
* @param loadFactor Initial load factor used used to create {@link HashMap}s for individual partitions.
*/
private IsolatedHashMap(final int initialCapacity, final float loadFactor) {
this.initialCapacity = initialCapacity;
this.loadFactor = loadFactor;
maps = new ConcurrentHashMap<>(8);
}
/**
* Get {@link Map} for current partition.
* @return {@link Map} for current partition. Will never return {@code null}.
*/
private Map<K, V> getMap() {
String partitionId = supportPartitions ? serverPlatform.getPartitionID() : DEFAULT_PARTITION_ID;
Map<K, V> partitionMap = maps.get(partitionId);
// First null check to skip locking when map is already initialized.
if (partitionMap == null) {
// FindBugs would be complaining about locking on maps so this is used to shut it up.
synchronized(this) {
// Second null check while having lock.
partitionMap = maps.get(partitionId);
if (partitionMap == null) {
partitionMap = new HashMap<>(initialCapacity, loadFactor);
maps.put(partitionId, partitionMap);
}
}
}
return partitionMap;
}
// All Map interface methods are delegated to Map mapped to current partition.
@Override
public int size() {
return getMap().size();
}
@Override
public boolean isEmpty() {
return getMap().isEmpty();
}
@Override
public boolean containsKey(Object key) {
return getMap().containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return getMap().containsValue(value);
}
@Override
public V get(Object key) {
return getMap().get(key);
}
@Override
public V put(K key, V value) {
return getMap().put(key, value);
}
@Override
public V remove(Object key) {
return getMap().remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
getMap().putAll(m);
}
@Override
public void clear() {
getMap().clear();
}
@Override
public Set<K> keySet() {
return getMap().keySet();
}
@Override
public Collection<V> values() {
return getMap().values();
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return getMap().entrySet();
}
}