Hi Brent,

I think all you need for test is this:


import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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;
        }
    }
}



Regards, Peter


On 05/04/2015 08:32 PM, Peter Levart wrote:


On 05/04/2015 06:11 PM, Brent Christian wrote:
Hi,

Please review this fix, courtesy of Peter Levart (thanks!), that I would like to get in.

https://bugs.openjdk.java.net/browse/JDK-8029891
http://cr.openjdk.java.net/~bchristi/8029891/webrev.0/

There is some discussion of it in the bug report, starting at 2014-12-31.

The problem, as stated by Mandy:

"System Properties is a hashtable that synchronizes on itself for any access. Currently System.getProperties returns the Properties instance accessed by the system in which any application code might synchronize on it (that's what the test is doing). The problem reported JDK-6977738 is about Properties.store method that was fixed not to synchronize on this instance. System property is a common way for changing the default setting and so it's impractical to expect the class loading code path not to call System.getProperty."

This fix changes java.util.Properties to store its values in an internal ConcurrentHashMap, ignoring its Hashtable heritage. In this way, Properties can be "de-sychronized": all methods inherited from Hashtable are overridden, to remove synchronization, and delegate to the internal CHM.

The serialized form is unchanged.


An alternative approach considered would be for System.getProperties() to return a duplicate snapshot of the current Properties. This presents a compatibility risk to existing code that keeps a reference to the return value of System.getProperties() and expects to either read new properties added afterwards, or set properties on the cached copy.

-Brent


Hi Brent,

The test looks mostly good, but I would refrain from matching the exception types as part of method signature. Why?

- javac already performs all the necessary checks about declared exceptions if some method overrides another method - overriding method can declare a subset and/or subtypes of checked exceptions of those than overridden method declares and this is OK. - overriding/overridden methods can declare arbitrary unrelated sets of unchecked exceptions and this is OK.

Another question is whether return type should be considered part of the method signature. For JVM it is, but Java, the language, just matches the method's name and parameter types and javac makes sure that overriding method declarest the same return type or a subtype of overridden method's return type. Well, javac generates two methods in the later case - the one declared in the source code with covariant return type and a synthetic bridge method with overridden method's return type that just calls the declared method. Class.getDeclaredMethods() returns both methods in that case. So either "key" shape would work (one that takes returnType as part of key and one that doesn't).

I don't think you need or even want to check that Properties declares all the methods with signatures from all interfaces. Just checking superclass(es) would do, as Properties is not abstract and in order to compile it must implement all abstract methods (either by inheriting from superclass(es) or implementing them itself). Specifically, you would not want to enforce interface default methods to be overridden by Properties if they are not overridden by superclass(es) (as the interface default method can only call other methods in the interface or Object methods and you need not override such method in Properties for Properties to be OK).

Regards, Peter


Reply via email to