mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-18 06:15:16 +00:00
8029891: Deadlock detected in java/lang/ClassLoader/deadlock/GetResource.java
Properties now stores values in an internal ConcurrentHashMap Reviewed-by: mchung, dholmes, plevart
This commit is contained in:
parent
6f2785f741
commit
0260528ef9
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -229,6 +229,14 @@ public class Hashtable<K,V>
|
||||
putAll(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor chained from {@link Properties} keeps Hashtable fields
|
||||
* uninitialized since they are not used.
|
||||
*
|
||||
* @param dummy a dummy parameter
|
||||
*/
|
||||
Hashtable(Void dummy) {}
|
||||
|
||||
/**
|
||||
* Returns the number of keys in this hashtable.
|
||||
*
|
||||
@ -549,18 +557,23 @@ public class Hashtable<K,V>
|
||||
* @return a clone of the hashtable
|
||||
*/
|
||||
public synchronized Object clone() {
|
||||
Hashtable<?,?> t = cloneHashtable();
|
||||
t.table = new Entry<?,?>[table.length];
|
||||
for (int i = table.length ; i-- > 0 ; ) {
|
||||
t.table[i] = (table[i] != null)
|
||||
? (Entry<?,?>) table[i].clone() : null;
|
||||
}
|
||||
t.keySet = null;
|
||||
t.entrySet = null;
|
||||
t.values = null;
|
||||
t.modCount = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
/** Calls super.clone() */
|
||||
final Hashtable<?,?> cloneHashtable() {
|
||||
try {
|
||||
Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
|
||||
t.table = new Entry<?,?>[table.length];
|
||||
for (int i = table.length ; i-- > 0 ; ) {
|
||||
t.table[i] = (table[i] != null)
|
||||
? (Entry<?,?>) table[i].clone() : null;
|
||||
}
|
||||
t.keySet = null;
|
||||
t.entrySet = null;
|
||||
t.values = null;
|
||||
t.modCount = 0;
|
||||
return t;
|
||||
return (Hashtable<?,?>)super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// this shouldn't happen, since we are Cloneable
|
||||
throw new InternalError(e);
|
||||
@ -1189,6 +1202,15 @@ public class Hashtable<K,V>
|
||||
*/
|
||||
private void writeObject(java.io.ObjectOutputStream s)
|
||||
throws IOException {
|
||||
writeHashtable(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform serialization of the Hashtable to an ObjectOutputStream.
|
||||
* The Properties class overrides this method.
|
||||
*/
|
||||
void writeHashtable(java.io.ObjectOutputStream s)
|
||||
throws IOException {
|
||||
Entry<Object, Object> entryStack = null;
|
||||
|
||||
synchronized (this) {
|
||||
@ -1218,12 +1240,30 @@ public class Hashtable<K,V>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by Properties to write out a simulated threshold and loadfactor.
|
||||
*/
|
||||
final void defaultWriteHashtable(java.io.ObjectOutputStream s, int length,
|
||||
float loadFactor) throws IOException {
|
||||
this.threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
|
||||
this.loadFactor = loadFactor;
|
||||
s.defaultWriteObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstitute the Hashtable from a stream (i.e., deserialize it).
|
||||
*/
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
throws IOException, ClassNotFoundException {
|
||||
readHashtable(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform deserialization of the Hashtable from an ObjectInputStream.
|
||||
* The Properties class overrides this method.
|
||||
*/
|
||||
void readHashtable(java.io.ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
// Read in the threshold and loadFactor
|
||||
s.defaultReadObject();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,6 +34,13 @@ import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.StreamCorruptedException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jdk.internal.util.xml.PropertiesDefaultHandler;
|
||||
|
||||
@ -60,6 +67,13 @@ import jdk.internal.util.xml.PropertiesDefaultHandler;
|
||||
* object that contains a non-{@code String} key.
|
||||
*
|
||||
* <p>
|
||||
* The iterators returned by the {@code iterator} method of this class's
|
||||
* "collection views" (that is, {@code entrySet()}, {@code keySet()}, and
|
||||
* {@code values()}) may not fail-fast (unlike the Hashtable implementation).
|
||||
* These iterators are guaranteed to traverse elements as they existed upon
|
||||
* construction exactly once, and may (but are not guaranteed to) reflect any
|
||||
* modifications subsequent to construction.
|
||||
* <p>
|
||||
* The {@link #load(java.io.Reader) load(Reader)} {@code /}
|
||||
* {@link #store(java.io.Writer, java.lang.String) store(Writer, String)}
|
||||
* methods load and store properties from and to a character based stream
|
||||
@ -127,6 +141,15 @@ class Properties extends Hashtable<Object,Object> {
|
||||
*/
|
||||
protected Properties defaults;
|
||||
|
||||
/**
|
||||
* Properties does not store values in its inherited Hashtable, but instead
|
||||
* in an internal ConcurrentHashMap. Synchronization is omitted from
|
||||
* simple read operations. Writes and bulk operations remain synchronized,
|
||||
* as in Hashtable.
|
||||
*/
|
||||
private transient ConcurrentHashMap<Object, Object> map =
|
||||
new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* Creates an empty property list with no default values.
|
||||
*/
|
||||
@ -140,6 +163,9 @@ class Properties extends Hashtable<Object,Object> {
|
||||
* @param defaults the defaults.
|
||||
*/
|
||||
public Properties(Properties defaults) {
|
||||
// use package-private constructor to
|
||||
// initialize unused fields with dummy values
|
||||
super((Void) null);
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
@ -826,9 +852,9 @@ class Properties extends Hashtable<Object,Object> {
|
||||
bw.write("#" + new Date().toString());
|
||||
bw.newLine();
|
||||
synchronized (this) {
|
||||
for (Enumeration<?> e = keys(); e.hasMoreElements();) {
|
||||
String key = (String)e.nextElement();
|
||||
String val = (String)get(key);
|
||||
for (Map.Entry<Object, Object> e : entrySet()) {
|
||||
String key = (String)e.getKey();
|
||||
String val = (String)e.getValue();
|
||||
key = saveConvert(key, true, escUnicode);
|
||||
/* No need to escape embedded and trailing spaces for value, hence
|
||||
* pass false to flag.
|
||||
@ -967,7 +993,7 @@ class Properties extends Hashtable<Object,Object> {
|
||||
* @see #defaults
|
||||
*/
|
||||
public String getProperty(String key) {
|
||||
Object oval = super.get(key);
|
||||
Object oval = map.get(key);
|
||||
String sval = (oval instanceof String) ? (String)oval : null;
|
||||
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
|
||||
}
|
||||
@ -1029,7 +1055,7 @@ class Properties extends Hashtable<Object,Object> {
|
||||
* @since 1.6
|
||||
*/
|
||||
public Set<String> stringPropertyNames() {
|
||||
Hashtable<String, String> h = new Hashtable<>();
|
||||
Map<String, String> h = new HashMap<>();
|
||||
enumerateStringProperties(h);
|
||||
return h.keySet();
|
||||
}
|
||||
@ -1044,11 +1070,11 @@ class Properties extends Hashtable<Object,Object> {
|
||||
*/
|
||||
public void list(PrintStream out) {
|
||||
out.println("-- listing properties --");
|
||||
Hashtable<String,Object> h = new Hashtable<>();
|
||||
Map<String, Object> h = new HashMap<>();
|
||||
enumerate(h);
|
||||
for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
|
||||
String key = e.nextElement();
|
||||
String val = (String)h.get(key);
|
||||
for (Map.Entry<String, Object> e : h.entrySet()) {
|
||||
String key = e.getKey();
|
||||
String val = (String)e.getValue();
|
||||
if (val.length() > 40) {
|
||||
val = val.substring(0, 37) + "...";
|
||||
}
|
||||
@ -1072,11 +1098,11 @@ class Properties extends Hashtable<Object,Object> {
|
||||
*/
|
||||
public void list(PrintWriter out) {
|
||||
out.println("-- listing properties --");
|
||||
Hashtable<String,Object> h = new Hashtable<>();
|
||||
Map<String, Object> h = new HashMap<>();
|
||||
enumerate(h);
|
||||
for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
|
||||
String key = e.nextElement();
|
||||
String val = (String)h.get(key);
|
||||
for (Map.Entry<String, Object> e : h.entrySet()) {
|
||||
String key = e.getKey();
|
||||
String val = (String)e.getValue();
|
||||
if (val.length() > 40) {
|
||||
val = val.substring(0, 37) + "...";
|
||||
}
|
||||
@ -1085,33 +1111,33 @@ class Properties extends Hashtable<Object,Object> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates all key/value pairs in the specified hashtable.
|
||||
* @param h the hashtable
|
||||
* Enumerates all key/value pairs into the specified Map.
|
||||
* @param h the Map
|
||||
* @throws ClassCastException if any of the property keys
|
||||
* is not of String type.
|
||||
*/
|
||||
private synchronized void enumerate(Hashtable<String,Object> h) {
|
||||
private void enumerate(Map<String, Object> h) {
|
||||
if (defaults != null) {
|
||||
defaults.enumerate(h);
|
||||
}
|
||||
for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
|
||||
String key = (String)e.nextElement();
|
||||
h.put(key, get(key));
|
||||
for (Map.Entry<Object, Object> e : entrySet()) {
|
||||
String key = (String)e.getKey();
|
||||
h.put(key, e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates all key/value pairs in the specified hashtable
|
||||
* Enumerates all key/value pairs into the specified Map
|
||||
* and omits the property if the key or value is not a string.
|
||||
* @param h the hashtable
|
||||
* @param h the Map
|
||||
*/
|
||||
private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
|
||||
private void enumerateStringProperties(Map<String, String> h) {
|
||||
if (defaults != null) {
|
||||
defaults.enumerateStringProperties(h);
|
||||
}
|
||||
for (Enumeration<?> e = keys() ; e.hasMoreElements() ;) {
|
||||
Object k = e.nextElement();
|
||||
Object v = get(k);
|
||||
for (Map.Entry<Object, Object> e : entrySet()) {
|
||||
Object k = e.getKey();
|
||||
Object v = e.getValue();
|
||||
if (k instanceof String && v instanceof String) {
|
||||
h.put((String) k, (String) v);
|
||||
}
|
||||
@ -1130,4 +1156,283 @@ class Properties extends Hashtable<Object,Object> {
|
||||
private static final char[] hexDigit = {
|
||||
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
||||
};
|
||||
|
||||
//
|
||||
// Hashtable methods overridden and delegated to a ConcurrentHashMap instance
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<Object> keys() {
|
||||
// CHM.keys() returns Iterator w/ remove() - instead wrap keySet()
|
||||
return Collections.enumeration(map.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<Object> elements() {
|
||||
// CHM.elements() returns Iterator w/ remove() - instead wrap values()
|
||||
return Collections.enumeration(map.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return map.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object put(Object key, Object value) {
|
||||
return map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object remove(Object key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void putAll(Map<?, ?> t) {
|
||||
map.putAll(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> keySet() {
|
||||
return Collections.synchronizedSet(map.keySet(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return Collections.synchronizedCollection(map.values(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
return Collections.synchronizedSet(new EntrySet(map.entrySet()), this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Properties.entrySet() should not support add/addAll, however
|
||||
* ConcurrentHashMap.entrySet() provides add/addAll. This class wraps the
|
||||
* Set returned from CHM, changing add/addAll to throw UOE.
|
||||
*/
|
||||
private static class EntrySet implements Set<Map.Entry<Object, Object>> {
|
||||
private Set<Map.Entry<Object,Object>> entrySet;
|
||||
|
||||
private EntrySet(Set<Map.Entry<Object, Object>> entrySet) {
|
||||
this.entrySet = entrySet;
|
||||
}
|
||||
|
||||
@Override public int size() { return entrySet.size(); }
|
||||
@Override public boolean isEmpty() { return entrySet.isEmpty(); }
|
||||
@Override public boolean contains(Object o) { return entrySet.contains(o); }
|
||||
@Override public Object[] toArray() { return entrySet.toArray(); }
|
||||
@Override public <T> T[] toArray(T[] a) { return entrySet.toArray(a); }
|
||||
@Override public void clear() { entrySet.clear(); }
|
||||
@Override public boolean remove(Object o) { return entrySet.remove(o); }
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<Object, Object> e) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Map.Entry<Object, Object>> c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return entrySet.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return entrySet.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return entrySet.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<Object, Object>> iterator() {
|
||||
return entrySet.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object o) {
|
||||
return map.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOrDefault(Object key, Object defaultValue) {
|
||||
return map.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
|
||||
map.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
|
||||
map.replaceAll(function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object putIfAbsent(Object key, Object value) {
|
||||
return map.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean remove(Object key, Object value) {
|
||||
return map.remove(key, value);
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Override
|
||||
public synchronized boolean replace(Object key, Object oldValue, Object newValue) {
|
||||
return map.replace(key, oldValue, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object replace(Object key, Object value) {
|
||||
return map.replace(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object computeIfAbsent(Object key,
|
||||
Function<? super Object, ?> mappingFunction) {
|
||||
return map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object computeIfPresent(Object key,
|
||||
BiFunction<? super Object, ? super Object, ?> remappingFunction) {
|
||||
return map.computeIfPresent(key, remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object compute(Object key,
|
||||
BiFunction<? super Object, ? super Object, ?> remappingFunction) {
|
||||
return map.compute(key, remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object merge(Object key, Object value,
|
||||
BiFunction<? super Object, ? super Object, ?> remappingFunction) {
|
||||
return map.merge(key, value, remappingFunction);
|
||||
}
|
||||
|
||||
//
|
||||
// Special Hashtable methods
|
||||
|
||||
@Override
|
||||
protected void rehash() { /* no-op */ }
|
||||
|
||||
@Override
|
||||
public synchronized Object clone() {
|
||||
Properties clone = (Properties) cloneHashtable();
|
||||
clone.map = new ConcurrentHashMap<>(map);
|
||||
return clone;
|
||||
}
|
||||
|
||||
//
|
||||
// Hashtable serialization overrides
|
||||
// (these should emit and consume Hashtable-compatible stream)
|
||||
|
||||
@Override
|
||||
void writeHashtable(ObjectOutputStream s) throws IOException {
|
||||
List<Object> entryStack = new ArrayList<>(map.size() * 2); // an estimate
|
||||
|
||||
for (Map.Entry<Object, Object> entry : map.entrySet()) {
|
||||
entryStack.add(entry.getValue());
|
||||
entryStack.add(entry.getKey());
|
||||
}
|
||||
|
||||
// Write out the simulated threshold, loadfactor
|
||||
float loadFactor = 0.75f;
|
||||
int count = entryStack.size() / 2;
|
||||
int length = (int)(count / loadFactor) + (count / 20) + 3;
|
||||
if (length > count && (length & 1) == 0) {
|
||||
length--;
|
||||
}
|
||||
synchronized (map) { // in case of multiple concurrent serializations
|
||||
defaultWriteHashtable(s, length, loadFactor);
|
||||
}
|
||||
|
||||
// Write out simulated length and real count of elements
|
||||
s.writeInt(length);
|
||||
s.writeInt(count);
|
||||
|
||||
// Write out the key/value objects from the stacked entries
|
||||
for (int i = entryStack.size() - 1; i >= 0; i--) {
|
||||
s.writeObject(entryStack.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void readHashtable(ObjectInputStream s) throws IOException,
|
||||
ClassNotFoundException {
|
||||
// Read in the threshold and loadfactor
|
||||
s.defaultReadObject();
|
||||
|
||||
// Read the original length of the array and number of elements
|
||||
int origlength = s.readInt();
|
||||
int elements = s.readInt();
|
||||
|
||||
// Validate # of elements
|
||||
if (elements < 0) {
|
||||
throw new StreamCorruptedException("Illegal # of Elements: " + elements);
|
||||
}
|
||||
|
||||
// create CHM of appropriate capacity
|
||||
map = new ConcurrentHashMap<>(elements);
|
||||
|
||||
// Read all the key/value objects
|
||||
for (; elements > 0; elements--) {
|
||||
Object key = s.readObject();
|
||||
Object value = s.readObject();
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,8 +126,6 @@ java/beans/Introspector/8132566/OverrideUserDefPropertyInfoTest.java 8132565 gen
|
||||
|
||||
# jdk_lang
|
||||
|
||||
java/lang/ClassLoader/deadlock/GetResource.java 8029891 generic-all
|
||||
|
||||
java/lang/StringCoding/CheckEncodings.sh 7008363 generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 2016 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/* @test
|
||||
* @bug 6977738
|
||||
* @bug 6977738 8029891
|
||||
* @summary Test ClassLoader.getResource() that should not deadlock
|
||||
# if another thread is holding the system properties object
|
||||
*
|
||||
@ -70,10 +70,6 @@ public class GetResource {
|
||||
go.await(); // wait until t1 holds the lock of the system properties
|
||||
|
||||
URL u1 = Thread.currentThread().getContextClassLoader().getResource("unknownresource");
|
||||
URL u2 = Thread.currentThread().getContextClassLoader().getResource("sun/util/resources/CalendarData.class");
|
||||
if (u2 == null) {
|
||||
throw new RuntimeException("Test failed: resource not found");
|
||||
}
|
||||
done.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
106
jdk/test/java/util/Properties/CheckOverrides.java
Normal file
106
jdk/test/java/util/Properties/CheckOverrides.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8029891
|
||||
* @summary Test that the Properties class overrides all public+protected
|
||||
* methods of all ancestor classes and interfaces
|
||||
* @run main CheckOverrides
|
||||
*/
|
||||
public class CheckOverrides {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<MethodSignature> pMethodSignatures =
|
||||
Stream.of(Properties.class.getDeclaredMethods())
|
||||
.filter(CheckOverrides::isMethodOfInterest)
|
||||
.map(MethodSignature::new)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<MethodSignature, Method> unoverriddenMethods = new HashMap<>();
|
||||
for (Class<?> superclass = Properties.class.getSuperclass();
|
||||
superclass != Object.class;
|
||||
superclass = superclass.getSuperclass()) {
|
||||
Stream.of(superclass.getDeclaredMethods())
|
||||
.filter(CheckOverrides::isMethodOfInterest)
|
||||
.forEach(m -> unoverriddenMethods.putIfAbsent(new MethodSignature(m), m));
|
||||
}
|
||||
unoverriddenMethods.keySet().removeAll(pMethodSignatures);
|
||||
|
||||
if (!unoverriddenMethods.isEmpty()) {
|
||||
throw new RuntimeException(
|
||||
"The following methods should be overridden by Properties class:\n" +
|
||||
unoverriddenMethods.values().stream()
|
||||
.map(Method::toString)
|
||||
.collect(Collectors.joining("\n ", " ", "\n"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isMethodOfInterest(Method method) {
|
||||
int mods = method.getModifiers();
|
||||
return !Modifier.isStatic(mods) &&
|
||||
(Modifier.isPublic(mods) || Modifier.isProtected(mods));
|
||||
}
|
||||
|
||||
static class MethodSignature {
|
||||
final Class<?> returnType;
|
||||
final String name;
|
||||
final Class<?>[] parameterTypes;
|
||||
|
||||
MethodSignature(Method method) {
|
||||
this(method.getReturnType(), method.getName(), method.getParameterTypes());
|
||||
}
|
||||
|
||||
private MethodSignature(Class<?> returnType, String name, Class<?>[] parameterTypes) {
|
||||
this.returnType = returnType;
|
||||
this.name = name;
|
||||
this.parameterTypes = parameterTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MethodSignature that = (MethodSignature) o;
|
||||
if (!returnType.equals(that.returnType)) return false;
|
||||
if (!name.equals(that.name)) return false;
|
||||
return Arrays.equals(parameterTypes, that.parameterTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = returnType.hashCode();
|
||||
result = 31 * result + name.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(parameterTypes);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
jdk/test/java/util/Properties/CheckUnsynchronized.java
Normal file
61
jdk/test/java/util/Properties/CheckUnsynchronized.java
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8029891
|
||||
* @summary Test Properties methods that do not synchronize any more
|
||||
* @run main CheckUnsynchronized
|
||||
*/
|
||||
public class CheckUnsynchronized {
|
||||
public static void main(String[] args) {
|
||||
Properties props = new Properties();
|
||||
synchronized (props) {
|
||||
props.setProperty("key", "value");
|
||||
System.out.println("contains(value)? " +
|
||||
CompletableFuture.supplyAsync(() -> props.contains("value")).join());
|
||||
System.out.println("containsKey(key)? " +
|
||||
CompletableFuture.supplyAsync(() -> props.containsKey("key")).join());
|
||||
System.out.println("containsValue(value)? " +
|
||||
CompletableFuture.supplyAsync(() -> props.containsValue("value")).join());
|
||||
Enumeration<Object> elems =
|
||||
CompletableFuture.supplyAsync(() -> props.elements()).join();
|
||||
System.out.println("first value from elements(): " + elems.nextElement());
|
||||
System.out.println("value from get(): " +
|
||||
CompletableFuture.supplyAsync(() -> props.getProperty("key")).join());
|
||||
System.out.println("getOrDefault(\"missing\"): " +
|
||||
CompletableFuture.supplyAsync(() -> props.getOrDefault("missing", "default")).join());
|
||||
System.out.println("isEmpty()? " +
|
||||
CompletableFuture.supplyAsync(() -> props.isEmpty()).join());
|
||||
Enumeration<Object> keys =
|
||||
CompletableFuture.supplyAsync(() -> props.keys()).join();
|
||||
System.out.println("first key from keys(): " + keys.nextElement());
|
||||
System.out.println("size(): " +
|
||||
CompletableFuture.supplyAsync(() -> props.size()).join());
|
||||
}
|
||||
}
|
||||
}
|
||||
148
jdk/test/java/util/Properties/PropertiesSerialization.java
Normal file
148
jdk/test/java/util/Properties/PropertiesSerialization.java
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8029891
|
||||
* @summary tests the compatibility of Properties serial form
|
||||
* @run main PropertiesSerialization read
|
||||
*
|
||||
* To update this test in case the serial form of Properties changes, run this
|
||||
* test with the 'write' flag, and copy the resulting output back into this
|
||||
* file, replacing the existing String declaration(s).
|
||||
*/
|
||||
public class PropertiesSerialization {
|
||||
private static final Properties TEST_PROPS;
|
||||
static {
|
||||
TEST_PROPS = new Properties();
|
||||
TEST_PROPS.setProperty("one", "two");
|
||||
TEST_PROPS.setProperty("buckle", "shoe");
|
||||
TEST_PROPS.setProperty("three", "four");
|
||||
TEST_PROPS.setProperty("shut", "door");
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encoded string for Properties object
|
||||
* Java version: 1.8.0
|
||||
**/
|
||||
private static final String TEST_SER_BASE64 =
|
||||
"rO0ABXNyABRqYXZhLnV0aWwuUHJvcGVydGllczkS0HpwNj6YAgABTAAIZGVmYXVs"
|
||||
+ "dHN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHIAE2phdmEudXRpbC5IYXNodGFi"
|
||||
+ "bGUTuw8lIUrkuAMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAI"
|
||||
+ "dwgAAAALAAAABHQAA29uZXQAA3R3b3QABHNodXR0AARkb29ydAAGYnVja2xldAAE"
|
||||
+ "c2hvZXQABXRocmVldAAEZm91cnhw";
|
||||
|
||||
public static void main(String[] args) throws IOException,
|
||||
ClassNotFoundException {
|
||||
if (args.length == 0) {
|
||||
System.err.println("Run with 'read' or 'write'");
|
||||
System.err.println(" read mode: normal test mode.");
|
||||
System.err.println(" Confirms that serial stream can");
|
||||
System.err.println(" be deserialized as expected.");
|
||||
System.err.println(" write mode: meant for updating the test,");
|
||||
System.err.println(" should the serial form change.");
|
||||
System.err.println(" Test output should be pasted");
|
||||
System.err.println(" back into the test source.");
|
||||
return;
|
||||
}
|
||||
|
||||
Properties deserializedObject;
|
||||
if ("read".equals(args[0])) {
|
||||
ByteArrayInputStream bais = new
|
||||
ByteArrayInputStream(Base64.getDecoder().decode(TEST_SER_BASE64));
|
||||
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
|
||||
deserializedObject = (Properties) ois.readObject();
|
||||
}
|
||||
if (!TEST_PROPS.equals(deserializedObject)) {
|
||||
throw new RuntimeException("deserializedObject not equals()");
|
||||
}
|
||||
System.out.println("Test passed");
|
||||
} else if ("write".equals(args[0])) {
|
||||
System.out.println("\nTo update the test, paste the following back "
|
||||
+ "into the test code:\n");
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(TEST_PROPS);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
|
||||
byte[] byteArray = baos.toByteArray();
|
||||
// Check that the Properties deserializes correctly
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
Properties deser = (Properties)ois.readObject();
|
||||
if (!TEST_PROPS.equals(deser)) {
|
||||
throw new RuntimeException("write: Deserialized != original");
|
||||
}
|
||||
|
||||
// Now get a Base64 string representation of the serialized bytes.
|
||||
final String base64 = Base64.getEncoder().encodeToString(byteArray);
|
||||
// Check that we can deserialize the Base64 string we just computed.
|
||||
ByteArrayInputStream bais2 =
|
||||
new ByteArrayInputStream(Base64.getDecoder().decode(base64));
|
||||
ObjectInputStream ois2 = new ObjectInputStream(bais2);
|
||||
Properties deser2 = (Properties)ois2.readObject();
|
||||
if (!TEST_PROPS.equals(deser2)) {
|
||||
throw new RuntimeException("write: Deserialized base64 != "
|
||||
+ "original");
|
||||
}
|
||||
System.out.println(dumpBase64SerialStream(base64));
|
||||
}
|
||||
}
|
||||
|
||||
private static final String INDENT = " ";
|
||||
/* Based on:
|
||||
* java/util/logging/HigherResolutionTimeStamps/SerializeLogRecored.java
|
||||
*/
|
||||
private static String dumpBase64SerialStream(String base64) {
|
||||
// Generates the Java Pseudo code that can be cut & pasted into
|
||||
// this test (see Jdk8SerializedLog and Jdk9SerializedLog below)
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(INDENT).append(" /**").append('\n');
|
||||
sb.append(INDENT).append(" * Base64 encoded string for Properties object\n");
|
||||
sb.append(INDENT).append(" * Java version: ")
|
||||
.append(System.getProperty("java.version")).append('\n');
|
||||
sb.append(INDENT).append(" **/").append('\n');
|
||||
sb.append(INDENT).append(" private static final String TEST_SER_BASE64 = ")
|
||||
.append("\n").append(INDENT).append(" ");
|
||||
final int last = base64.length() - 1;
|
||||
for (int i=0; i<base64.length();i++) {
|
||||
if (i%64 == 0) sb.append("\"");
|
||||
sb.append(base64.charAt(i));
|
||||
if (i%64 == 63 || i == last) {
|
||||
sb.append("\"");
|
||||
if (i == last) sb.append(";\n");
|
||||
else sb.append("\n").append(INDENT).append(" + ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user