Discussing the design, decisions and discovery involved in developing Mutability Detector, an open-source analysis tool for Java.

Sunday, 15 July 2012

Checking for Mutability in JSR 310: ZoneRulesGroup

In seeing how Mutability Detector fared on ThreeTen, the reference implementation for JSR 310, the previous two blogs described an in depth analysis of classes which cause failing immutability tests. Hopefully the following examples will be a bit more simple, and clear cut. 

The assertion:
      assertImmutable(ZoneRulesGroup.class); 

Fails with the message:
org.mutabilitydetector.unittesting.MutabilityAssertionError: 
Expected: javax.time.zone.ZoneRulesGroup to be IMMUTABLE
     but: javax.time.zone.ZoneRulesGroup is actually NOT_IMMUTABLE
    Reasons:
        Field is not final, if shared across threads the Java Memory Model will not guarantee it is initialised before it is read. [Field: versions, Class: javax.time.zone.ZoneRulesGroup]
        Field can have a mutable type (java.util.concurrent.atomic.AtomicReference) assigned to it. [Field: versions, Class: javax.time.zone.ZoneRulesGroup]
        Field can have a mutable type (java.lang.String) assigned to it. [Field: groupID, Class: javax.time.zone.ZoneRulesGroup]
    Allowed reasons:
        None.
It's becomming a common and irritating theme, but we can suppress the String Problem as per the previous examples. That leaves us with two issues: that ZoneRulesGroup has a non-final field, and that same field is a mutable type (AtomicReference).

The declaration of the field in question looks like this:

private AtomicReference<TreeMap<String, ZoneRulesVersion>> versions =
            new AtomicReference<TreeMap<String, ZoneRulesVersion>>(new TreeMap<String, ZoneRulesVersion>(Collections.reverseOrder()));

The field itself contains a mutable type, but it's another example of an unobservable caching stretegy. There is also extra synchronization around modification, but thread safety != immutability, and Mutability Detector isn't intended to analyse this. The other complaint from Mutability Detector is that the field may not be visible if published to other threads. This however, is a special case that Mutability Detector does not account for. As described in Java Concurrency In Practice (p52, ch3.5.3 Safe Publication Idioms) 

"A properly constructed object can be safely published by [...] Storing a reference to it into a volatile field or AtomicReference". 

The aforementioned synchronization would also guarantee visibility.


 VERDICT: False positive.

 WHAT TO DO?: I don't see any reason to not make the field final, since it is never reassigned anyway. Thus we could add the final modifier to the declaration. You could say that it's overkill, and just noise, but I think the semantics of how the field is used matches the final keyword, so I'd be happy to add it. To solve the mutable field problem, we could borrow the same allowed reason from an earlier example. The assertion would look like this:
assertInstancesOf(javax.time.zone.ZoneRulesGroup.class,
                  areImmutable(),
                  provided(String.class).isAlsoImmutable(),
                  assumingTheFields("versions").areModifiedAsPartAsAnUnobservableCachingStrategy());
If there's some reason that the field cannot be final that I don't understand, Mutability Detector ships with an allowed reason implementation which can help here. Keeping the field non-final, an assertion could be:
assertInstancesOf(javax.time.zone.ZoneRulesGroup.class,
                  areImmutable(),
                  provided(String.class).isAlsoImmutable(),
                 assumingTheFields("versions").areModifiedAsPartAsAnUnobservableCachingStrategy(),
                  allowingNonFinalFields());
But, "allowingNonFinalFields()" can be overly permissive, allowing new non-final fields to be added in such a way that they don't guarantee safe publication. Perhaps it would be better to specify the field by name, as with the other allowed reason.
For me, this false positive is a good find. In terms of visibility in the Java Memory Model, Mutability Detector could fairly easily permit a field if it is either volatile or an instance of AtomicReference.

Since I'm keeping score, it's ThreeTen: 2, Mutability Detector 1. Next up, ZoneOffsetTransitionRule.

Checking for Mutability in JSR 310: ResourceZoneRulesDataProvider


Checking for Mutability in JSR 310: ResourceZoneRulesDataProvider

Carrying on in my checking of mutability in the reference implementation of JSR 310, ThreeTen, I'm going to look at the next failing test case.

The assertion[0]:

 assertImmutable(Class.forName("javax.time.zone.ResourceZoneRulesDataProvider"));

Fails with:
  org.mutabilitydetector.unittesting.MutabilityAssertionError: 
  Expected: javax.time.zone.ResourceZoneRulesDataProvider to be IMMUTABLE
      but: javax.time.zone.ResourceZoneRulesDataProvider is actually NOT_IMMUTABLE
      Reasons:
   Field can have a mutable type (java.lang.String) assigned to it. [Field: groupID, Class: javax.time.zone.ResourceZoneRulesDataProvider]
   Field can have a mutable type (java.util.HashSet) assigned to it. [Field: regions, Class: javax.time.zone.ResourceZoneRulesDataProvider]
   Field can have a mutable type (java.util.HashSet) assigned to it. [Field: versions, Class: javax.time.zone.ResourceZoneRulesDataProvider]
   Field can have a mutable type (java.util.concurrent.atomic.AtomicReferenceArray) assigned to it. [Field: rules, Class: javax.time.zone.ResourceZoneRulesDataProvider]
      Allowed reasons:
   None.
The first failure reason, exemplifies The String Problem. Which I can suppress by changing the assertion to:

  assertInstancesOf(Class.forName("javax.time.zone.ResourceZoneRulesDataProvider"), 
   areImmutable(),
   provided(String.class).isAlsoImmutable());
 
Which leaves us with the three remaining failure reasons. The first two are quite similar, that the class has mutable fields of type java.util.Set (the assignment is from the concrete java.util.HashSet). Although we can't tell from the bytecode, the generic type of these Sets are String, and a ThreeTen type, ZoneRulesVersion, which is expected to be immutable.Thus, element types of both fields can be considered immutable. This means that in terms of these fields, we only have to worry about the collection type, not the element type.

At first glance, this appears to be a problem. Here's a snippet which represents a subset of the code in the ResourceZoneRulesDataProvider class:

final class ResourceZoneRulesDataProvider {
  private final Set<ZoneRulesVersion> versions;
  private final Set<String> regions;
  
  private ResourceZoneRulesDataProvider(URL url) {
    // constructor code reading from an input stream
    this.regions = new HashSet<String>(Arrays.asList(aLocallyPopulatedArrayVariable));
    this.versions = aLocallyPopulatedHashSet;
  }
  
  public Set<ZoneRulesVersion> getVersions() {
    return versions;
  }

  public Set<String> getRegionIDs() {
    return regions;
  }
}


Taken at face value, this code represents a problem. If a user could get a hold of an instance of ResourceZoneRulesDataProvider, they could for instance, execute:

 resourceZoneRulesDataProvider.getRegionIDs().remove("Europe/London");

This represents a trade off in Mutability Detector's design, and the choice to not perform whole program analysis. Those methods to retrieve the mutable sets are only invoked from one place, the class ZoneRulesGroup. This class is in the same package, taking advantage of the package-scoped methods in ResourceZoneRulesDataProvider. Tracing up those calls, it appears they are only triggered from the static initialisation block from ZoneRulesGroup, from which neither the ResourceZoneRulesDataProvider instances, or their mutable fields escape. Such a situation would take much more analysis than Mutability Detector currently performs, increasing the time taken to analyse, and greatly complicate the implementation. That's not likely to be an avenue I'd be willing to take, so the next best thing is to offer users the ability to overrule Mutability Detector, and tell the tool that this particular example is safe.

Fortunately, an API for doing so already exists in Mutability Detector, it just takes a bit more effort from the user. It involves implementing a Hamcrest matcher to allow mutability reasons. In the assertion example above, the call "provided(String.class).isAlsoImmutable()" is nothing more than just a Hamcrest matcher. An example of how that could look is:

static final class AssumingTheFields  {
    
    private Set<String> fieldNames;

    private AssumingTheFields(Set<String> fieldNames) {
 this.fieldNames = fieldNames;
    }
    
    public static AssumingTheFields assumingTheFields(String first, String... rest) {
 return new AssumingTheFields(Sets.newHashSet(concat(asList(first), asList(rest))));
    }
    
    Matcher<MutableReasonDetail> areNotModifiedByCallers() {
 return new TypeSafeDiagnosingMatcher<MutableReasonDetail>() {
     @Override public void describeTo(Description description) { }

     @Override
     protected boolean matchesSafely(MutableReasonDetail item, Description mismatchDescription) {
  CodeLocation<?> locationOfMutability = item.codeLocation();
  if (locationOfMutability instanceof FieldLocation) {
      return item.reason().isOneOf(MUTABLE_TYPE_TO_FIELD)
       && fieldNames.contains(((FieldLocation)locationOfMutability).fieldName());
  } else {
      return false;
  }
     }
 };
    }
}

This allows us to write an assertion like this:

    assertInstancesOf(Class.forName("javax.time.zone.ResourceZoneRulesDataProvider"), 
                      areImmutable(),
                      provided(String.class).isAlsoImmutable(),
                      assumingTheFields("regions", "versions").areNotModifiedByCallers());
                   
("regions" and "versions" being the Sets which escape from ResourceZoneRulesDataProvider). The result of this assertion now looks like this:
org.mutabilitydetector.unittesting.MutabilityAssertionError: 
Expected: javax.time.zone.ResourceZoneRulesDataProvider to be IMMUTABLE
     but: javax.time.zone.ResourceZoneRulesDataProvider is actually NOT_IMMUTABLE
    Reasons:
        Field can have a mutable type (java.util.concurrent.atomic.AtomicReferenceArray) assigned to it. [Field: rules, Class: javax.time.zone.ResourceZoneRulesDataProvider]
    Allowed reasons:
        Field can have a mutable type (java.lang.String) assigned to it. [Field: groupID, Class: javax.time.zone.ResourceZoneRulesDataProvider]
        Field can have a mutable type (java.util.HashSet) assigned to it. [Field: regions, Class: javax.time.zone.ResourceZoneRulesDataProvider]
        Field can have a mutable type (java.util.HashSet) assigned to it. [Field: versions, Class: javax.time.zone.ResourceZoneRulesDataProvider]
Allowing these fields to be mutable leaves us with just one failure. In this case the "rules" field, which is of type AtomicReferenceArray. This is a very similar problem to the previous one. In this case, the collection does not escape, but its contents are modified in one of the instance methods. This appears to be implementation detail of a caching strategy, not an observable change. Again, the scope to invoke this method is limited, and with the current state of the code, it looks to be used safely. As such, we can just treat this field like the escaping Sets, so our assertion can look like this:

  assertInstancesOf(Class.forName("javax.time.zone.ResourceZoneRulesDataProvider"), 
                    areImmutable(),
                    provided(String.class).isAlsoImmutable(),
                    assumingTheFields("regions", "versions").areNotModifiedByCallers(),
                    assumingTheFields("rules").areModifiedAsPartAsAnUnobservableCachingStrategy());
                 
Which passes. Note that the implementation for both suppressions are the same, the difference in name is to communicate to other developers.

VERDICT: This case is tricky. It's a false positive in the context of whole program analysis. However, the class in isolation is definitely mutable. Classes within the same package have the necessary access to create and mutate instances of this class.
WHAT TO DO?: It is possible to suppress the reasons to get a passing test. Personally, I would either: stop claiming the class is immutable in JavaDoc, and document that instances "depend on the kindness of strangers" as it were, to be effectively immutable; or be more strict, and wrap mutable collection types in unmodifiable versions[1].


In conclusion, in a blatant show of bias, I'm going to consider this a very narrow win for Mutability Detector.That put's us at ThreeTen: 1, Mutability Detector: 1.

Click here for the next entry, ZoneRulesGroup.


[0] This assertion demonstrates using the String version of the method, which is useful when your test is for a class which is out of scope (e.g. private, protected, etc).
[1] Though there are two issues with that: the overhead in creating an unmodifiable wrapper; and that Mutability Detector doesn't yet correctly analyse defensive copies into unmodifiable wrappers, so users would still have to add a suppression anyway.




Times Have Changed, But An Instant Stays The Same: Checking for Mutability in JSR 310


I recently attended a London Java Community meetup to try out the new date and time API scheduled for Java 8[1]. JSR 310 (or "ThreeTen" as named on GitHub) provides an upgrade to the standard date and time libraries available in the JDK. At first use, ThreeTen looks to have addressed the mistakes of old JDK Date API, and draws influence from the popular, defacto standard date/time library, JodaTime[2].

Overall I was very impressed with the design and discoverability of the API. And of course, I'm a big fan of it's design choice to use immutability wherever possible.

Since JodaTime is somewhat of a poster-child for immutability in Java, and it's unsurprising the same design principle has transferred to ThreeTen. I was curious to see how effective Mutability Detector could be in helping to enforce this principle.

Using the highly rigourous and scientific method of grep'ing the codebase for the term " immutable ", as included in the JavaDoc for such types, I found 55 immutable classes in ThreeTen. For each of those 55, I added a unit test case to assert the class is immutable. Out of those 55, 6 tests are failing. Among the passing tests are some of ThreeTen's core classes: LocalDate, LocalDateTime, Duration, Period, and more. I'm really impressed by how 'cleanly' these immutable implementations are. I'm also happy with the apparent success rate of Mutability Detector on this real-world codebase (though I know that the results assume zero false negatives).

There's two ways to look at 6 failing tests from 55. Firstly, assuming that when a ThreeTen author documents a class as immutable, it is, Mutability Detector produces 6 false positives. Secondly, assuming that Mutability Detector correctly analyzes, ThreeTen has unexpected mutability in 6 classes. Since these conditions are mutually exclusive, over the next few blog entries, I'm going to take a look at the classes in question. First up is DateTimeFormatter.

The assertion:

  assertImmutable(javax.time.format.DateTimeFormatter.class);

Fails with:

  org.mutabilitydetector.unittesting.MutabilityAssertionError: 
  Expected: javax.time.format.DateTimeFormatter to be IMMUTABLE
      but: javax.time.format.DateTimeFormatter is actually NOT_IMMUTABLE
      Reasons:
 Field can have a mutable type (java.util.Locale) assigned to it. [Field: locale, Class: javax.time.format.DateTimeFormatter]
 Field can have a mutable type (javax.time.format.DateTimeFormatterBuilder$CompositePrinterParser) assigned to it. [Field: printerParser, Class: javax.time.format.DateTimeFormatter]
      Allowed reasons:
 None.

DateTimeFormatter is considered mutable because it has two fields of mutable types. The types in question being java.util.Locale, and a ThreeTen internal type, CompositePrinterParser.  Since the result for Locale (for the JDK 1.6u25 incarnation at least) is a false positive relating to The String Problem, which I can suppress in the unit test, I'll take a more detailed look at CompositePrinterParser.

The problem that Mutability Detector has with this class is that it contains a field which is an array type. There are several ways of (ab)using an array field that breaks immutability. The class can: (A) modify the contents of the array field after construction; (B) let a mutable element of the array escape to where it can be modified; (C) let the array escape so that callers can modify the contents; (D) take the array passed to the constructor without performing a defensive copy. Note that Mutability Detector isn't yet capable of this kind of analysis, so it just panics at the sight of an array field, and leaves it up to us, the developer, to investigate.

Out of those potential problems described:

(A) doesn't happen. The class only iterates over the array, it doesn't modify the contents.
(B) isn't applicable here. The element type of the array is an interface which is contracted to be immutable.
(C) and (D) are the interesting parts. There is no getter for the array field, and it's private. The only way the array escapes, is that it is passed as the constructor argument to another instance of the same class. Since we know that same class doesn't modify the array, the destination for the escaping array is a "safe haven". That particular constructor is also only ever called by the same class, so it doesn't need to copy the passed in array. There is a second constructor, but that takes a List, and performs a defensive copy of it, so the class really is in control of that array field.

VERDICT: False Positive

WHAT TO DO?:
For both Locale, and CompositePrinterParser, we can add suppressions, or "allowed reasons" for mutability. Thus the test case can look like this:

assertInstancesOf(javax.time.format.DateTimeFormatter.class, 
                  areImmutable(), 
                  provided(Locale.class).isAlsoImmutable(),
                  provided("javax.time.format.DateTimeFormatterBuilder$CompositePrinterParser").isAlsoImmutable());
               
This will allow the test to pass, ignoring the false positives, but failing again if someone introduces mutability another way.
               
Score so far, ThreeTen 1, Mutability Detector 0. Next up, ResourceZoneRulesDataProvider. (Click here for the next blog entry).

[1] Thanks to OpenGamma, Stephen Colebourne, Richard Warburton, James Gough et al for organising.
[2] This is unsurprising, since one of the technical spec leads of JSR 310, Stephen Colebourne is the creator of JodaTime.




Thursday, 1 March 2012

Announcing Version 0.8 and MutabilityDetector4FindBugs


Version 0.8 of MutabilityDetector has been released and is now available from Maven Central. This release includes bug fixes for the following issues:

Probably the most significant change is that Mutability Detector now recognizes when you have prevented subclassing by only having private constructors (previously you had to mark your class final).

I'm also very pleased to announce the release of a FindBugs plugin for Mutability Detector. This is also available from Maven Central and the project's download page. Just configure your FindBugs installation to pickup the plugin detector during analysis (usually by placing it in the 'plugin' directory) and it will detect classes annotated with @Immutable. The detector will emit a FindBugs warning for each violation of the rules for achieving immutability.

Disclaimer, the Mutability Detector FindBugs plugin is marked as version 0.2 for a reason. It's a first cut, and since I personally use Mutability Detector as a unit testing tool, I don't plan to 'dog food' the plugin so much. So any and all feedback is welcome. Please feel free to email me (Grundlefleck at gmail dot com), file an issue, or post to the Google group.

Friday, 30 December 2011

Imprudence Begets Impetus - No Go For GoDaddy

It's funny how unrelated things can finally give you the motivation to check off an item that's been on the TODO list for a while. In this case it was the online movement to boycott GoDaddy, due to their stance on SOPA.

One of the items I've been meaning to get to Real Soon Now, was to register a domain for Mutability Detector. If for no other reason than making the org.mutabilitydetector package name in the source code actually match Java conventions.

Since I had no existing GoDaddy accounts, I couldn't lend my support to the boycott by moving my domains away. But I could vote with my wallet and go with a competitor. Enter Namecheap, another domain name registrar like GoDaddy, but with a much more amenable outlook on SOPA. Since this was the first domain name purchase I've made, and I'm currently only using it as a redirect, I can't comment on the service in too much detail. But the process was cheap and convenient and their interface was quite friendly and easy to use.

I might do more with www.mutabilitydetector.org in the future, but for now it just redirects to the project homepage, and gives me an ever-so-slight warm and fuzzy for doing a teensy-tiny bit in the efforts against SOPA.

Wednesday, 16 November 2011

Version 0.7 Released

Mutability Detector v0.7 has been released. It is available for download at the project homepage, or from Maven Central with the details:

Group ID Artifact ID Version
org.mutabilitydetector MutabilityDetector 0.7

This release included improvements in two areas: improve support allowing reasons for mutability in unit tests,  and the introduction of analysis on the 'this' reference escaping. It also includes more documentation to help get started with unit testing for immutability (view online or as JavaDoc embedded in the sources jar).

Other more minor improvements include:
Any and all feedback welcome.

Also, if you do use Mutability Detector, and your source is accessible, please get in touch. When releasing new versions I want to have as little inconvenience for users as possible, and currently I only judge this using a pretend client codebase. Potentially I could add your project to my continuous integration setup, and test my trunk code against yours to give more realistic feedback.

There's already a couple of issues I want to include in the next release (issues 17, 18, 19 at least) and of course, there's always "The String Problem", but if there's anything that's affecting you, please do get in touch.



Wednesday, 9 November 2011

New EscapedThisReferenceChecker due for next release

In a previous post I discussed a new check that Mutability Detector should make to provide a more accurate result. The implementation of that checker is now complete, and will be included in an upcoming 0.7 release.

As with any other checks the tool does, the analysis is unsound, that is, it doesn't guarantee correctness. It makes as best a guess as it can, and I hope that empirical study of its use would show that it's good enough to rely on, but it is possible that the tool can be fooled into emitting false positives or false negatives. I thought it would be useful to talk about the checker does and doesn't do.

The checker analyses every constructor of your class, and checks for the following problems.


Assigning 'this' to a field
The tool will render your class mutable if it does any of the following:
  1. assigns 'this' to static field 
    • SomeOtherClass.staticField = this;
  2. assigns 'this' to instance of other object
    • someOtherObject.instanceField = this;
  3. assigns 'this' to field of this instance
    • this.myField = this;
  4. assigns 'this' to static field of this class
    • ThisClass.staticField = this;
Some of these will be a false positive, e.g. with #2, if you construct an object in the constructor, and assign 'this' to one of its fields, before it's published, it'll be safe.  Also assigning 'this' to an instance field is safe as well (or at least, I can't think why it is unsafe), but it's weird enough that I don't want to lose too much sleep trying to be correct about it.


Passing 'this' as a parameter

The tool will render your class mutable if it does any of the following:

  1. passes 'this' as parameter to another method
    • new SomeOtherClass(this);
    • SomeOtherClass.staticMethod(this);
    • someOtherObject.instanceMethod(this);
  2. constructs an inner class (which has an implicit reference to 'this')
    • new InnerClass()
A false positive in this case could be, with #1, passing 'this' to somewhere that only saves the reference, without trying to read any values from it, or with #2, the instance of the inner class might not escape, meaning the 'this' reference hasn't escaped.

Unexpected false positives
The last examples of false positives are ones that can occur with a correct implementation. However, there's one false positive that I didn't expect to create. While the tool is trying to check for 'this' being passed to other methods, it also accidentally raises an issue when instance fields are passed as well.


Here's an example from java.util.Vector:

public Vector(Collection c) {
    elementData = c.toArray();
    elementCount = elementData.length;

    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }
}



The issue being the call to Arrays.copyOf() with two instance fields. This piece of code will cause Vector to be reported as mutable (among many other reasons), and quite nicely demonstrates the issues involved. Here, the method is single threaded, but it hypothetically could change at any point to publish the references it receives to multiple threads. Without further (expensive) analysis into what Arrays.copyOf() does, I can't conclude whether Vector is immutable or not. I'm choosing to err on the side of caution. There is the scope for a potential whitelist of safe methods, but that's not on the cards just yet.


Another interesting point is that one field is a primitive int, the other is an array. Since the value of the int will be copied to the method, it's probably safe anyway, no matter whether threads are involved or not. However, for the array, the reference to the field will be passed, not the contents. If the array is not completely populated and made visible when it's passed, the receiving method could observe it to change, breaking immutability. This leads on to the wider question of any mutable fields escaping at any point in the object's life cycle, not just in the constructor. For now, I'm embracing the happy accident that the checker will warn you of some potentially dodgy code, and will deliberately leave that particular false positive active.


I don't expect any of these false positives to really cause an issue If you disagree, get in touch, email me, leave a comment, or raise a bug (that may require sign in).



I expect to release a new version within the next week, stay tuned for more news!