On Android, the co-variant override of ConcurrentHashMap.keySet()
introduced in Java 8 was undone. [1] If compiled Java code calls that
co-variant override directly, one gets a NoSuchMethodError exception
at run-time on Android.
Making the code call that method via Map.keySet() side-steps this
problem.
This is similar to bug 496262, where the same problem cropped up when
compiling with Java 8 against a Java 7 target, but here we cannot use
bootclasspath. We build against Java 8, not against the Android version
of it.
Recent Android versions should have some bytecode "magic" that adds the
co-variant override in bytecode (see the commit referenced in [1]), but
on older Android version this problem may still occur. (Or perhaps the
"magic" is ineffective...) There are two pull requests on Github for
this problem, both from 2020, [2][3] while the Android commit [1] is
from March 2018. Apparently people still occasionally run into this
problem in the wild.
[1] 0e8b937ded/ojluni/src/main/java/java/util/concurrent/ConcurrentHashMap.java (1244)
[2] https://github.com/eclipse/jgit/pull/104
[3] https://github.com/eclipse/jgit/pull/100
Change-Id: I7c07e0cc59871cb7fe60795e22867827fa9c2458
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
tags/v5.11.0.202102031030-m2
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.ConcurrentMap; | |||||
import org.eclipse.jetty.http.HttpVersion; | import org.eclipse.jetty.http.HttpVersion; | ||||
import org.eclipse.jetty.security.AbstractLoginService; | import org.eclipse.jetty.security.AbstractLoginService; | ||||
static class TestMappedLoginService extends AbstractLoginService { | static class TestMappedLoginService extends AbstractLoginService { | ||||
private String role; | private String role; | ||||
protected final ConcurrentMap<String, UserPrincipal> users = new ConcurrentHashMap<>(); | |||||
protected final Map<String, UserPrincipal> users = new ConcurrentHashMap<>(); | |||||
TestMappedLoginService(String role) { | TestMappedLoginService(String role) { | ||||
this.role = role; | this.role = role; |
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.ConcurrentMap; | |||||
import org.eclipse.jgit.api.errors.GitAPIException; | import org.eclipse.jgit.api.errors.GitAPIException; | ||||
import org.eclipse.jgit.api.errors.JGitInternalException; | import org.eclipse.jgit.api.errors.JGitInternalException; | ||||
* Available archival formats (corresponding to values for | * Available archival formats (corresponding to values for | ||||
* the --format= option) | * the --format= option) | ||||
*/ | */ | ||||
private static final ConcurrentMap<String, FormatEntry> formats = | |||||
private static final Map<String, FormatEntry> formats = | |||||
new ConcurrentHashMap<>(); | new ConcurrentHashMap<>(); | ||||
/** | /** | ||||
* @param newValue value to be associated with the key (null to remove). | * @param newValue value to be associated with the key (null to remove). | ||||
* @return true if the value was replaced | * @return true if the value was replaced | ||||
*/ | */ | ||||
private static <K, V> boolean replace(ConcurrentMap<K, V> map, | |||||
private static <K, V> boolean replace(Map<K, V> map, | |||||
K key, V oldValue, V newValue) { | K key, V oldValue, V newValue) { | ||||
if (oldValue == null && newValue == null) // Nothing to do. | if (oldValue == null && newValue == null) // Nothing to do. | ||||
return true; | return true; |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.Map; | |||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
* @since 4.6 | * @since 4.6 | ||||
*/ | */ | ||||
public class FilterCommandRegistry { | public class FilterCommandRegistry { | ||||
private static ConcurrentHashMap<String, FilterCommandFactory> filterCommandRegistry = new ConcurrentHashMap<>(); | |||||
private static Map<String, FilterCommandFactory> filterCommandRegistry = new ConcurrentHashMap<>(); | |||||
/** | /** | ||||
* Register a {@link org.eclipse.jgit.attributes.FilterCommandFactory} | * Register a {@link org.eclipse.jgit.attributes.FilterCommandFactory} |
package org.eclipse.jgit.events; | package org.eclipse.jgit.events; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.ConcurrentMap; | |||||
import java.util.concurrent.CopyOnWriteArrayList; | import java.util.concurrent.CopyOnWriteArrayList; | ||||
/** | /** | ||||
* Manages a thread-safe list of {@link org.eclipse.jgit.events.RepositoryListener}s. | * Manages a thread-safe list of {@link org.eclipse.jgit.events.RepositoryListener}s. | ||||
*/ | */ | ||||
public class ListenerList { | public class ListenerList { | ||||
private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>(); | |||||
private final Map<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>(); | |||||
/** | /** | ||||
* Register a {@link org.eclipse.jgit.events.WorkingTreeModifiedListener}. | * Register a {@link org.eclipse.jgit.events.WorkingTreeModifiedListener}. |
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import java.util.concurrent.ScheduledFuture; | import java.util.concurrent.ScheduledFuture; | ||||
import java.util.concurrent.ScheduledThreadPoolExecutor; | import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
cache.configureEviction(repositoryCacheConfig); | cache.configureEviction(repositoryCacheConfig); | ||||
} | } | ||||
private final ConcurrentHashMap<Key, Repository> cacheMap; | |||||
private final Map<Key, Repository> cacheMap; | |||||
private final Lock[] openLocks; | private final Lock[] openLocks; | ||||
package org.eclipse.jgit.nls; | package org.eclipse.jgit.nls; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import org.eclipse.jgit.errors.TranslationBundleLoadingException; | import org.eclipse.jgit.errors.TranslationBundleLoadingException; | ||||
} | } | ||||
private final Locale locale; | private final Locale locale; | ||||
private final ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>(); | |||||
private final Map<Class, TranslationBundle> map = new ConcurrentHashMap<>(); | |||||
private NLS(Locale locale) { | private NLS(Locale locale) { | ||||
this.locale = locale; | this.locale = locale; |